docs: redesign the guide site, fix inaccurate docs

- new modern layout for index.html / index.it.html with shared
  style.css + main.js: app-window mock, pipeline diagram, searchable
  troubleshooting, copy buttons on build commands, live star count
- stream port is 5599 (not 9xxx); real log line is
  "ERRORE caricamento dll"; button label is STOP
- troubleshooting covers more real log lines (UAC refused, pldmgr
  missing, elfldr unreachable, stream dropped)
- links to .md guides point to GitHub, download goes to /releases
  (v0.1 is a pre-release, /latest would miss it)
This commit is contained in:
fstat13 committed 2026-10-15 15:28:12 +02:00
1 parent 07fe4968da
commit 876f8ef82b
7 files changed
+1304 -627

No files matched your search

+1 -1
View File
@@ -83,7 +83,7 @@ payload/ PS5 side: display-engine reader + NV12 streamer (pscapture.elf)
pc/ PSCapture.exe — GUI + installer + payload deployer + feeder
pc/ps5cam.c the virtual capture-card driver (DirectShow source filter)
pc/shm.h shared-memory contract between feeder and driver
docs/ guides: HTML, fool-proof step-by-step, build, installer, RE, log
docs/ guides: HTML, step-by-step, build, installer, RE, log
```
## 🔧 Build
+4 -4
View File
@@ -1,4 +1,4 @@
# PSCapture — la guida a prova di scemo
# PSCapture — la guida passo-passo completa
Da zero alla tua PS5 dentro Discord. Ogni passo dice esattamente cosa devi
vedere sullo schermo. Se qualcosa non torna, salta alla tabella dei problemi
@@ -47,7 +47,7 @@ per Windows sono dentro di lui.
| `driver gia' installato` | la camera è già registrata — ok |
| `driver estratto dall'eseguibile (… byte)` + `driver registrato - la webcam 'PS5 Capture' e' pronta` | primo avvio: driver installato |
| `payload dall'eseguibile (… byte)` + `payload avviato - la PS5 deve mostrare la notifica PSCapture` | il payload sta girando sulla console |
| `porta stream: 9xxx` poi `collegato a 192.168.0.36:9xxx` | stream connesso |
| `porta stream: 5599` poi `collegato a 192.168.0.36:5599` | stream connesso |
| la pillola diventa **verde**: `STREAMING - scegli 'PS5 Capture' in Discord` | sei live |
5. Sulla PS5 compare la notifica di sistema: **PSCapture is streaming on:PORT**.
@@ -74,7 +74,7 @@ Qui va alla piena risoluzione **1920x1080**.
payload è già sulla console (autoloader) lo stream parte subito; se la
console è spenta la pillola dice *"IN ATTESA DELLA PS5"* e si connette nel
momento in cui la console è pronta.
- Premi **Stop** (o chiudi l'app) per fermare lo stream. La camera si congela
- Premi **STOP** (o chiudi l'app) per fermare lo stream. La camera si congela
sull'ultimo frame; gli altri programmi continuano a vedere il dispositivo.
- **Deploy payload** rimanda il payload a mano (utile dopo un riavvio della
console senza autoloader). **Install driver** riregistra la camera a mano
@@ -98,7 +98,7 @@ Entrambe si applicano in diretta (circa un secondo) e sono salvate in
| Il log ripete `PS5 non raggiungibile (...), riprovo` | come sopra | come sopra |
| La camera `PS5 Capture` non appare in Discord/OBS | driver mai registrato (install fallito) | premi **Install driver**; leggi la riga di log che segue — ora riporta l'errore *vero* |
| Log: `ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)` | app non elevata | chiudi e riavvia `PSCapture.exe` accettando il prompt UAC |
| Log: `ERRORE caricando la dll (err=126)` | l'antivirus ha messo in quarantena la dll estratta | controlla la quarantena dell'antivirus per `pscapture.dll`, sbloccala, premi di nuovo **Install driver** |
| Log: `ERRORE caricamento dll (err=126)` | l'antivirus ha messo in quarantena la dll estratta | controlla la quarantena dell'antivirus per `pscapture.dll`, sbloccala, premi di nuovo **Install driver** |
| La camera appare ma l'immagine è nera | stream non partito | controlla la pillola; premi **Deploy payload** e/o **START STREAMING** |
| I colori sembrano sbagliati (pelle rossastra, cielo arancione) | qualcuno ha toccato `gain` | rimetti `gain` a `1.0` → Apply |
| Immagine decentrata / barra nera | allineamento | regola `shift px` con numeri pari finché non è centrata |
+4 -4
View File
@@ -1,4 +1,4 @@
# PSCapture — the fool-proof guide
# PSCapture — the complete step-by-step guide
🇮🇹 Versione italiana: [GUIDE.it.md](GUIDE.it.md)
@@ -46,7 +46,7 @@ the Windows capture-card driver are inside it.
| `driver gia' installato` | camera already registered — fine |
| `driver estratto dall'eseguibile (… byte)` + `driver registrato - la webcam 'PS5 Capture' e' pronta` | first run: driver installed |
| `payload dall'eseguibile (… byte)` + `payload avviato - la PS5 deve mostrare la notifica PSCapture` | the payload is now running on the console |
| `porta stream: 9xxx` then `collegato a 192.168.0.36:9xxx` | stream connected |
| `porta stream: 5599` then `collegato a 192.168.0.36:5599` | stream connected |
| status pill turns **green**: `STREAMING - scegli 'PS5 Capture' in Discord` | you're live |
5. The PS5 shows a system notification: **PSCapture is streaming on:PORT**.
@@ -72,7 +72,7 @@ This runs at the full **1920x1080**.
itself: if the payload is already on the console (autoloader) the stream
starts by itself; if the console is off the pill says
*"IN ATTESA DELLA PS5"* and connects the moment the console is ready.
- Press **Stop** (or close the app) to stop streaming. The camera goes black /
- Press **STOP** (or close the app) to stop streaming. The camera goes black /
freezes on the last frame; consumers keep the device listed.
- **Deploy payload** re-sends the payload by hand (useful after a console
reboot without autoloader). **Install driver** re-registers the camera by
@@ -96,7 +96,7 @@ exe, so they survive restarts.
| Log repeats `PS5 non raggiungibile (...)`, riprova | same as above | same |
| No `PS5 Capture` camera appears in Discord/OBS | driver never registered (install failed) | press **Install driver**; read the log line after it — it now reports the *real* error |
| Log: `ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)` | app not elevated | close it, restart `PSCapture.exe` and accept the UAC prompt |
| Log: `ERRORE caricando la dll (err=126)` | antivirus quarantined the extracted dll | check the antivirus quarantine for `pscapture.dll`, allow it, press **Install driver** again |
| Log: `ERRORE caricamento dll (err=126)` | antivirus quarantined the extracted dll | check the antivirus quarantine for `pscapture.dll`, allow it, press **Install driver** again |
| Camera listed but the image is black | stream not running | check the pill; press **Deploy payload** and/or **START STREAMING** |
| Colors look wrong (skin reddish, sky orange) | `gain` was changed | set `gain` back to `1.0` → Apply |
| Image has a black bar / is off-center | alignment | tune `shift px` (even values) until centered |
+395 -305
View File
@@ -1,274 +1,350 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PSCapture by fstat13 — PS5 screen as a capture card on PC</title>
<style>
:root{
--bg:#0d1015; --card:#151a22; --card2:#1b222d; --line:#242c38;
--fg:#e8edf4; --dim:#96a0b0; --accent:#0070d1; --accent2:#35a3ff;
--rec:#e22236; --ok:#1fbf6b;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);
font:16px/1.65 "Segoe UI",system-ui,-apple-system,Roboto,sans-serif;}
a{color:var(--accent2);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:880px;margin:0 auto;padding:0 20px}
header{background:
radial-gradient(900px 320px at 20% -40%, rgba(0,112,209,.35), transparent 60%),
radial-gradient(700px 300px at 90% -30%, rgba(226,34,54,.22), transparent 60%),
var(--bg);
border-bottom:1px solid var(--line);padding:56px 0 40px}
.brand{display:flex;align-items:center;gap:18px}
.brand img{width:84px;height:84px;border-radius:20px;box-shadow:0 8px 30px rgba(0,0,0,.5)}
h1{font-size:44px;margin:0;letter-spacing:.5px}
.tag{color:var(--dim);font-size:18px;margin-top:6px}
.badges{margin-top:22px;display:flex;gap:10px;flex-wrap:wrap}
.badge{border:1px solid var(--line);background:var(--card);
padding:5px 12px;border-radius:999px;font-size:13px;color:var(--dim)}
.badge b{color:var(--fg)}
h2{margin:52px 0 14px;font-size:26px}
h3{margin:26px 0 10px;font-size:19px}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px 20px}
.card h3{margin:0 0 8px;font-size:17px}
.card p{margin:0;color:var(--dim);font-size:14.5px}
.flow{display:flex;gap:10px;align-items:stretch;flex-wrap:wrap;margin-top:16px}
.box{flex:1;min-width:180px;background:var(--card2);border:1px solid var(--line);
border-radius:12px;padding:14px 16px}
.box b{display:block;margin-bottom:6px}
.box div{color:var(--dim);font-size:13.5px}
.arrow{align-self:center;color:var(--accent2);font-size:22px;padding:0 2px}
ol.steps{counter-reset:s;list-style:none;padding:0;margin:18px 0 0}
ol.steps li{counter-increment:s;position:relative;padding:0 0 18px 54px}
ol.steps li::before{content:counter(s);position:absolute;left:0;top:0;
width:34px;height:34px;border-radius:10px;background:var(--accent);
color:#fff;font-weight:600;display:flex;align-items:center;justify-content:center}
ol.steps b{color:var(--fg)}
ol.steps span{color:var(--dim)}
ul.plain{color:var(--dim);padding-left:22px}
ul.plain b{color:var(--fg)}
table{width:100%;border-collapse:collapse;margin-top:14px;font-size:14.5px}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);
vertical-align:top}
th{color:var(--dim);font-weight:500}
td code,th code{white-space:nowrap}
code,kbd{background:#0a0d12;border:1px solid var(--line);border-radius:6px;
padding:1px 7px;font:13.5px/1.5 Consolas,monospace;color:#cfe3ff}
pre{background:#0a0d12;border:1px solid var(--line);border-radius:12px;
padding:14px 16px;overflow-x:auto;font:13.5px/1.6 Consolas,monospace;
color:#cfe3ff}
pre .c{color:#5f7186}
details{background:var(--card);border:1px solid var(--line);border-radius:12px;
padding:14px 18px;margin:10px 0}
summary{cursor:pointer;font-weight:600}
details p{color:var(--dim);margin:10px 0 0}
.note{background:linear-gradient(0deg,rgba(0,112,209,.08),rgba(0,112,209,.08)),
var(--card);border:1px solid #23405f;border-radius:12px;padding:14px 18px;color:#cfe3ff}
.warn{background:linear-gradient(0deg,rgba(226,34,54,.07),rgba(226,34,54,.07)),
var(--card);border:1px solid #5f2330;border-radius:12px;padding:14px 18px;color:#f3cdd3}
footer{color:var(--dim);border-top:1px solid var(--line);margin-top:70px;
padding:26px 0 40px;font-size:13.5px}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;
background:var(--rec);margin-right:8px;vertical-align:1px}
.ok{color:var(--ok);font-weight:600}
.toc{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
.toc a{border:1px solid var(--line);background:var(--card);border-radius:999px;
padding:4px 12px;font-size:13px}
.lang{position:absolute;top:18px;right:22px;font-size:13.5px}
</style>
<title>PSCapture — your PS5 screen as a capture card on PC</title>
<meta name="description" content="PSCapture turns a jailbroken PS5 into a capture card for your PC: the console screen streams over your LAN and shows up in Discord, OBS and ffmpeg as a camera called “PS5 Capture”.">
<meta name="theme-color" content="#07090d">
<link rel="icon" type="image/png" href="pscapture.png">
<link rel="alternate" hreflang="en" href="https://fstat13.github.io/PSCapture/">
<link rel="alternate" hreflang="it" href="https://fstat13.github.io/PSCapture/index.it.html">
<meta property="og:type" content="website">
<meta property="og:title" content="PSCapture — your PS5 screen, live on your PC">
<meta property="og:description" content="The PS5 screen shows up in Discord, OBS and ffmpeg as the camera “PS5 Capture”. No capture card, no Remote Play, no cloud.">
<meta property="og:image" content="https://fstat13.github.io/PSCapture/pscapture.png">
<meta property="og:url" content="https://fstat13.github.io/PSCapture/">
<meta name="twitter:card" content="summary">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header style="position:relative">
<div class="lang">🇮🇹 <a href="index.it.html">Italiano</a></div>
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="i-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
<symbol id="i-star" viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></symbol>
<symbol id="i-video" viewBox="0 0 24 24"><path d="m22 8-6 4 6 4V8Z"/><rect x="2" y="6" width="14" height="12" rx="2"/></symbol>
<symbol id="i-zap" viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></symbol>
<symbol id="i-file" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
<symbol id="i-console" viewBox="0 0 24 24"><rect x="2" y="6" width="20" height="12" rx="3"/><line x1="6" y1="12" x2="10" y2="12"/><line x1="8" y1="10" x2="8" y2="14"/><circle cx="16" cy="11" r=".6"/><circle cx="18" cy="13" r=".6"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></symbol>
<symbol id="i-alert" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
</svg>
<nav class="nav">
<div class="wrap">
<div class="brand">
<img src="pscapture.png" alt="PSCapture">
<div>
<h1>PSCapture</h1>
<div class="tag">The PlayStation&nbsp;5 screen, live on your PC — as a real capture card.</div>
<a class="logo" href="#top"><img src="pscapture.png" alt="">PSCapture</a>
<div class="nav-links">
<a href="#how">How it works</a>
<a href="#start">Setup</a>
<a href="#trouble">Troubleshooting</a>
<a href="#build">Build</a>
<a href="#faq">FAQ</a>
</div>
<div class="nav-right">
<a class="lang" href="index.it.html" hreflang="it">Italiano</a>
<a class="btn btn-ghost btn-sm" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Star <span class="stars" data-stars hidden></span></a>
</div>
</div>
</nav>
<header class="hero" id="top">
<div class="wrap hero-grid">
<div>
<span class="eyebrow"><span class="tagv">v0.1</span>Tested on PS5 firmware 13.60</span>
<h1>Your PS5 screen,<br><span class="grad">live on your PC.</span></h1>
<p class="lead">PSCapture turns a jailbroken PlayStation&nbsp;5 into a capture card.
The console streams its own screen over your LAN, and Windows sees it as a
camera called <b>PS5 Capture</b>, ready for Discord, OBS and ffmpeg.</p>
<div class="cta">
<a class="btn btn-primary" href="https://github.com/fstat13/PSCapture/releases"><svg class="i"><use href="#i-download"/></svg>Download for Windows</a>
<a class="btn btn-ghost" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Star on GitHub <span class="stars" data-stars hidden></span></a>
</div>
<div class="hero-meta">
<span><svg class="i"><use href="#i-check"/></svg>No capture card</span>
<span><svg class="i"><use href="#i-check"/></svg>No Remote Play</span>
<span><svg class="i"><use href="#i-check"/></svg>No cloud</span>
<span><svg class="i"><use href="#i-check"/></svg>One .exe</span>
</div>
</div>
<div class="badges">
<span class="badge"><b>PS5</b> jailbroken</span>
<span class="badge"><b>Windows</b> 10 / 11</span>
<span class="badge"><span class="dot"></span><b>No</b> capture card</span>
<span class="badge"><b>No</b> Remote&nbsp;Play</span>
<span class="badge">1080p &middot; ~30 fps</span>
<div class="app" role="img" aria-label="The PSCapture window: the log shows the driver check, the payload being sent and the stream connecting; the status pill reads STREAMING.">
<div class="app-bar">
<img src="pscapture.png" alt="">
<span class="t">PSCapture by fstat13 - PS5 screen to PC</span>
<span class="w"><span>–</span><span>□</span><span>✕</span></span>
</div>
<div class="app-body">
<div class="app-head">
<div class="app-title">PSCapture<small>PS5 screen to PC</small></div>
<span class="pill live"><i></i> STREAMING 29.8 fps</span>
</div>
<div class="app-row">
<span class="app-lbl">PS5 IP</span><span class="app-in">192.168.0.36</span>
<span class="app-btn">Deploy payload</span><span class="app-btn">Install driver</span>
</div>
<div class="app-row">
<span class="app-btn big">START STREAMING</span><span class="app-btn stop">STOP</span>
<span class="app-knobs app-row" style="margin:0">
<span class="app-lbl">gain</span><span class="app-in">1.0</span>
<span class="app-lbl">shift px</span><span class="app-in">0</span>
</span>
</div>
<div class="app-log" aria-hidden="true">
<div>PSCapture pronto: premo Start e faccio tutto da solo.</div>
<div>--- AVVIO AUTOMATICO ---</div>
<div>driver gia' installato</div>
<div>payload dall'eseguibile (175480 byte)</div>
<div>payload 175480 byte, invio a 192.168.0.36</div>
<div>autoloader aggiornato (pldmgr)</div>
<div class="hl">payload avviato - la PS5 deve mostrare la notifica PSCapture</div>
<div>porta stream: 5599</div>
<div class="ok">collegato a 192.168.0.36:5599</div>
</div>
</div>
</div>
<div class="toc">
<a href="#start">Quick start</a>
<a href="#daily">Daily use</a>
<a href="#trouble">Troubleshooting</a>
<a href="#build">Build from source</a>
<a href="#faq">FAQ</a>
</div>
<div class="wrap">
<div class="stats">
<div class="stat"><div class="n">1920×1080</div><div class="l">full HD in OBS</div></div>
<div class="stat"><div class="n">~30 fps</div><div class="l">over a plain LAN</div></div>
<div class="stat"><div class="n">0</div><div class="l">re-encodes on the console</div></div>
<div class="stat"><div class="n">1 file</div><div class="l">driver and payload inside</div></div>
</div>
</div>
</header>
<div class="wrap">
<main class="wrap">
<h2>What it is</h2>
<div class="cards">
<div class="card">
<h3>🎥 Virtual capture card</h3>
<p>PSCapture registers a DirectShow device named <b>PS5 Capture</b>.
Discord, OBS and ffmpeg see it like an Elgato — pick it and go live.</p>
<section id="why" class="reveal">
<div class="sec-head">
<div class="kicker">Why PSCapture</div>
<h2>A capture card, without the capture card.</h2>
<p class="sub">Everything runs on hardware you already own: the console, your network and your PC.</p>
</div>
<div class="card">
<h3>⚡ Straight from the GPU</h3>
<p>The console payload reads the display engine's scan-out buffer directly
and streams NV12 frames over your LAN. No re-encode delay, no cloud.</p>
<div class="grid g3">
<div class="card"><div class="ico"><svg class="i"><use href="#i-video"/></svg></div>
<h3>A real camera device</h3>
<p>A DirectShow source filter named <b>PS5 Capture</b>. Any app that can open a webcam can open your console.</p></div>
<div class="card"><div class="ico v"><svg class="i"><use href="#i-zap"/></svg></div>
<h3>Straight from the display engine</h3>
<p>The payload reads the console's scan-out buffer and sends raw NV12 frames. Nothing is re-encoded, so nothing waits for an encoder.</p></div>
<div class="card"><div class="ico g"><svg class="i"><use href="#i-file"/></svg></div>
<h3>One file, one window</h3>
<p><b>PSCapture.exe</b> installs the driver, sends the payload, registers it in the autoloader and connects, all on its own.</p></div>
<div class="card"><div class="ico a"><svg class="i"><use href="#i-refresh"/></svg></div>
<h3>Recovers by itself</h3>
<p>Console still off? It retries every 2 seconds. Stream dropped? It reconnects. You don't have to babysit it.</p></div>
<div class="card"><div class="ico"><svg class="i"><use href="#i-sliders"/></svg></div>
<h3>Live tuning</h3>
<p>Colour <code>gain</code> and horizontal <code>shift</code> apply in about a second and are saved in <code>pscapture.ini</code>.</p></div>
<div class="card"><div class="ico v"><svg class="i"><use href="#i-lock"/></svg></div>
<h3>Stays on your network</h3>
<p>No accounts, no cloud relay, no Remote Play. Frames go from the console to your PC and nowhere else.</p></div>
</div>
<div class="card">
<h3>🖱️ One executable</h3>
<p><b>PSCapture.exe</b> carries the console payload inside itself:
install the driver, push the payload, start streaming — one window,
one click.</p>
</section>
<section id="how" class="reveal">
<div class="sec-head">
<div class="kicker">How it works</div>
<h2>Three small pieces, one camera.</h2>
<p class="sub">A payload on the console, a feeder and a driver on the PC. The apps you already use just see a webcam.</p>
</div>
</div>
<div class="pipe">
<div class="node"><div class="where">PS5</div><h3>Payload</h3>
<p>Reads the display engine's scan-out ring, builds NV12 frames and serves them on TCP <code>5599</code>. Pops the toast <i>“PSCapture is streaming on:5599”</i>.</p></div>
<div class="link"><span>TCP · NV12<br>your LAN</span><div class="line"></div></div>
<div class="node"><div class="where">PC · PSCapture.exe</div><h3>Feeder</h3>
<p>Receives the frames and publishes them into a shared-memory ring.</p></div>
<div class="link"><span>shared<br>memory</span><div class="line"></div></div>
<div class="node"><div class="where">PC · pscapture.dll</div><h3>Driver</h3>
<p>A DirectShow source filter that exposes the ring as the camera <b>PS5 Capture</b>.</p></div>
<div class="link"><span>DirectShow</span><div class="line"></div></div>
<div class="node end"><div class="where">Your apps</div><h3>Any camera app</h3>
<p>Pick <b>PS5 Capture</b> like any webcam.</p>
<div class="chips"><span class="chip">Discord</span><span class="chip">OBS</span><span class="chip">ffmpeg</span></div></div>
</div>
</section>
<h2>How it works</h2>
<div class="flow">
<div class="box"><b>PS5 &middot; payload</b>
<div>reads the DCE scan-out ring, builds NV12 frames, serves them on a TCP
port, pops a toast: <i>“PSCapture is streaming on:PORT”</i>.</div></div>
<div class="arrow">→</div>
<div class="box"><b>PC &middot; feeder</b>
<div>receives frames and publishes them into a shared-memory ring.</div></div>
<div class="arrow">→</div>
<div class="box"><b>PC &middot; driver</b>
<div>DirectShow filter exposes the ring as a camera every app can use.</div></div>
</div>
<section id="requirements" class="reveal">
<div class="sec-head">
<div class="kicker">Before you start</div>
<h2>What you need.</h2>
</div>
<div class="grid g2">
<div class="req">
<div class="req-h"><div class="ico"><svg class="i"><use href="#i-console"/></svg></div><h3>On the PS5</h3></div>
<ul>
<li><svg class="i"><use href="#i-check"/></svg><span>A jailbroken console<small>Built and tuned on firmware <b>13.60</b>. See the FAQ for other versions.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Payload loader on TCP <code>9021</code><small><code>elfldr</code>-style, as in every current jailbreak setup.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Anonymous FTP on TCP <code>2121</code><small>The app reads and writes a few files on the console.</small></span></li>
<li class="opt"><svg class="i"><use href="#i-check"/></svg><span><code>pldmgr</code> autoloader <span class="tagopt">optional</span><small>Starts the payload on every boot, so you never deploy by hand.</small></span></li>
</ul>
</div>
<div class="req">
<div class="req-h"><div class="ico v"><svg class="i"><use href="#i-monitor"/></svg></div><h3>On the PC</h3></div>
<ul>
<li><svg class="i"><use href="#i-check"/></svg><span>Windows 10 or 11, 64-bit<small>The camera is a DirectShow device, so the PC side is Windows-only.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Same network as the console<small>Wired recommended. Gigabit is plenty for 1080p at 30 fps.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Admin rights, once<small>To register the camera for every program on the machine.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span><b>PSCapture.exe</b> from <a href="https://github.com/fstat13/PSCapture/releases">Releases</a><small>One single file: the payload and the driver are inside it.</small></span></li>
</ul>
</div>
</div>
</section>
<h2 id="start">Quick start — step by step</h2>
<section id="start" class="reveal">
<div class="sec-head">
<div class="kicker">Setup</div>
<h2>From zero to live in five steps.</h2>
<p class="sub">After step 2 you mostly watch: the app does the rest and tells you what it's doing.</p>
</div>
<ol class="steps">
<li><h3>Jailbreak the console</h3>
<p>Run your usual jailbreak so the payload loader (<code>9021</code>) and FTP (<code>2121</code>) are up.</p></li>
<li><h3>Double-click <code>PSCapture.exe</code></h3>
<p>Windows asks <i>“Do you want to allow this app to make changes to your device?”</i>. Click <b>Yes</b>. The app elevates itself, no right-click needed.</p></li>
<li><h3>Check the IP</h3>
<p>The default is <code>192.168.0.36</code>. If your PS5 has a different address, type it in <b>PS5 IP</b> and press <b>Apply</b>. It's remembered. You can find it on the console under <i>Settings → Network → Connection Status → View Connection Status</i>.</p></li>
<li><h3>Hands off</h3>
<p>On the first run the app installs the driver, then sends the payload and connects. The console shows <i>“PSCapture is streaming on:5599”</i> and the status pill turns <b style="color:var(--green)">green: STREAMING</b>.</p></li>
<li><h3>Pick <b>PS5 Capture</b> in your app</h3>
<p>Discord, OBS or ffmpeg. See <a href="#use">where to find it</a> below.</p></li>
</ol>
<div class="note" style="margin-bottom:20px">
<b>What you need first.</b> A jailbroken PS5 with the standard setup:
a payload loader on TCP <code>9021</code> (<code>elfldr</code>-style),
anonymous FTP on TCP <code>2121</code>, and optionally the
<code>pldmgr</code> autoloader so the payload starts on every boot.
PC and console on the same LAN. Download <b>PSCapture.exe</b> from the
Releases page — <b>it's one single file</b>: the console payload and the
Windows driver are inside it.
</div>
<h3 style="margin:30px 0 6px">Reading the log</h3>
<p style="color:var(--dim)">The app's log is in Italian. Here's what each line means, in the order you'll see them.</p>
<div class="term" style="margin-top:14px">
<div class="term-bar"><i></i><i></i><i></i><b>pscapture-app.log</b></div>
<div class="term-row"><code>driver gia' installato</code><span class="m">The camera is already registered. Nothing to do.</span></div>
<div class="term-row"><code>driver non installato: lo installo ora (primo avvio)<br>driver estratto dall'eseguibile (… byte)<br>driver registrato - la webcam 'PS5 Capture' e' pronta</code><span class="m">First run: the driver is unpacked from the exe and registered.</span></div>
<div class="term-row"><code>payload dall'eseguibile (… byte)<br>payload … byte, invio a 192.168.0.36</code><span class="m">The payload is on its way to the console.</span></div>
<div class="term-row"><code>autoloader aggiornato (pldmgr)</code><span class="m">The payload was copied to the autoloader, so it'll start on every boot.</span></div>
<div class="term-row"><code>payload avviato - la PS5 deve mostrare la notifica PSCapture</code><span class="m">The payload is running. Look for the toast on the TV.</span></div>
<div class="term-row ok"><code>porta stream: 5599<br>collegato a 192.168.0.36:5599</code><span class="m">Connected. The pill reads <b>STREAMING</b> with the live fps.</span></div>
</div>
<ol class="steps">
<li><b>Jailbreak the console</b><br>
<span>Any kernel-capable jailbreak that gives you the payload loader
(:9021), anonymous FTP (:2121) and — optionally — the
<code>pldmgr</code> autoloader.</span></li>
<li><b>Double-click <code>PSCapture.exe</code></b><br>
<span>Windows asks <i>“Do you want to allow this app to make changes?”</i>
→ <b>Yes</b>. The app elevates itself; admin is needed once, to register
the virtual camera for every program on the machine.</span></li>
<li><b>Check the IP</b><br>
<span>If your PS5 is not at <code>192.168.0.36</code>, type its address
in the <b>PS5 IP</b> field and press <b>Apply</b>.</span></li>
<li><b>Watch it work — press nothing</b><br>
<span>The app does everything alone, and narrates it in the dark log pane
(see the table below). On the console you will also see the system
notification <i>“PSCapture is streaming on:PORT”</i>.</span></li>
<li><b>Use it in any app</b><br>
<span>Discord (call): <i>Settings → Voice &amp; Video → Camera</i> →
<b>PS5 Capture</b> (720p — Discord asks every camera for 720p, capture
cards included). OBS: add a <i>Video Capture Device</i> → full
<b>1920x1080</b>. ffmpeg:
<code>-f dshow -i video="PS5 Capture"</code>.</span></li>
</ol>
<div class="callout warn" style="margin-top:18px">
<svg class="i"><use href="#i-alert"/></svg>
<p><b>Pill stuck on “IN ATTESA DELLA PS5”</b> (“waiting for the PS5”)? The console is off, not jailbroken yet, or the IP is wrong. You don't need to restart anything: the app retries every 2 seconds and connects the moment ports <code>9021</code> and <code>2121</code> answer.</p>
</div>
</section>
<h3>What you should see in the app log</h3>
<table>
<tr><th>Line</th><th>Meaning</th></tr>
<tr><td><code>driver gia' installato</code></td>
<td>camera already registered — nothing to do</td></tr>
<tr><td><code>driver estratto dall'eseguibile (… byte)</code><br>
<code>driver registrato - la webcam 'PS5 Capture' e' pronta</code></td>
<td>first run: the driver was unpacked from the exe and registered</td></tr>
<tr><td><code>payload dall'eseguibile (… byte)</code></td>
<td>the console payload is on its way to the PS5</td></tr>
<tr><td><code>payload avviato - la PS5 deve mostrare la notifica PSCapture</code></td>
<td>the payload is running on the console</td></tr>
<tr><td><code>porta stream: 9xxx</code> → <code>collegato a …:9xxx</code></td>
<td>stream connected — the pill turns <span class="ok">green: STREAMING</span></td></tr>
</table>
<section id="use" class="reveal">
<div class="sec-head">
<div class="kicker">Use it</div>
<h2>Works wherever a webcam works.</h2>
</div>
<div class="grid g3">
<div class="card target"><h3>Discord</h3>
<div class="path"><i>Settings → Voice &amp; Video → Camera</i> → <b>PS5 Capture</b></div>
<p style="margin-top:10px">Discord asks every camera for 720p, capture cards included. For 1080p60 use screen-share (Nitro).</p>
<div class="res">in calls <b>1280×720</b></div></div>
<div class="card target"><h3>OBS Studio</h3>
<div class="path"><i>Sources → + → Video Capture Device</i> → <b>PS5 Capture</b></div>
<p style="margin-top:10px">Full resolution, ready for recording or streaming.</p>
<div class="res">native <b>1920×1080</b></div></div>
<div class="card target"><h3>ffmpeg</h3>
<div class="path"><code>ffmpeg -f dshow -i video="PS5 Capture" capture.mkv</code></div>
<p style="margin-top:10px">Scripted captures and quick tests.</p>
<div class="res">native <b>1920×1080</b></div></div>
</div>
</section>
<div class="warn" style="margin-top:18px">
<b>Pill stuck on “IN ATTESA DELLA PS5 — accesa e col jailbreak avviato?”</b><br>
The console is off, not jailbroken yet, or the IP is wrong. The app keeps
retrying every 2 seconds: the moment the console is ready it connects by
itself. Ports to check on the console: <code>9021</code> and
<code>2121</code> must be open.
</div>
<section id="daily" class="reveal">
<div class="sec-head">
<div class="kicker">Daily use</div>
<h2>Open it and play.</h2>
</div>
<div class="grid g3">
<div class="card knob"><span class="k">every day</span>
<p>Open <b>PSCapture.exe</b> and accept the UAC prompt. With the autoloader the stream starts by itself.</p>
<ul>
<li><b>Deploy payload</b>: re-sends the payload by hand, after a reboot without autoloader.</li>
<li><b>Install driver</b>: re-registers the camera if the log shows a driver error.</li>
<li><b>STOP</b> or closing the app: the camera freezes on the last frame and stays listed.</li>
</ul></div>
<div class="card knob"><span class="k">gain</span>
<p>Colour (chroma) strength.</p>
<ul><li><code>1.0</code> is raw and correct.</li><li>Only touch it if colours look off.</li></ul></div>
<div class="card knob"><span class="k">shift px</span>
<p>Moves the picture left or right.</p>
<ul><li><b>Even numbers only</b>: an odd value swaps the colours.</li><li>The console's title text makes a good reference.</li></ul></div>
</div>
</section>
<h2 id="daily">Daily use &amp; tuning</h2>
<ul class="plain">
<li>Open <b>PSCapture.exe</b> (UAC → Yes). If the payload is already on the
console (autoloader), the stream starts by itself; otherwise press
<b>Deploy payload</b> once.</li>
<li><b>Stop</b> / closing the app stops the stream — the camera freezes on
the last frame and consumers keep the device listed.</li>
<li><b>gain</b> — chroma strength. <b>Leave it at 1.0</b> (raw) unless
colors look off.</li>
<li><b>shift px</b> — horizontal alignment. <b>Even numbers only</b> (an
odd value inverts the colors). Applies live, saved in
<code>pscapture.ini</code> next to the exe.</li>
</ul>
<section id="trouble" class="reveal">
<div class="sec-head">
<div class="kicker">Troubleshooting</div>
<h2>Symptom → fix.</h2>
<p class="sub">Type what you see, or paste a line from the log.</p>
</div>
<div class="search"><svg class="i"><use href="#i-search"/></svg>
<input id="q" type="search" placeholder="e.g. black, 720p, err=126, ATTESA…" autocomplete="off" aria-label="Filter problems"></div>
<div class="issues">
<div class="issue"><div class="sym">Pill stays on <code>AVVIO…</code> or <code>IN ATTESA DELLA PS5</code></div>
<div class="fix"><span class="cause">console off, not jailbroken, or wrong IP</span>Turn the console on, run the jailbreak (ports 9021 and 2121 must be up), check the <b>PS5 IP</b> field. The app keeps retrying.</div></div>
<div class="issue"><div class="sym">Log repeats <code>PS5 non raggiungibile (…), riprovo</code></div>
<div class="fix"><span class="cause">console not reachable</span>Same as above. It connects by itself as soon as the console answers.</div></div>
<div class="issue"><div class="sym">Log: <code>elfldr :9021 non raggiungibile</code></div>
<div class="fix"><span class="cause">payload loader not running</span>Run the jailbreak again, then press <b>Deploy payload</b>.</div></div>
<div class="issue"><div class="sym">No <b>PS5 Capture</b> camera in Discord or OBS</div>
<div class="fix"><span class="cause">driver not registered</span>Press <b>Install driver</b> and read the next log line: it reports the real error.</div></div>
<div class="issue"><div class="sym">Log: <code>ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)</code></div>
<div class="fix"><span class="cause">app not elevated</span>Close it, start <code>PSCapture.exe</code> again and answer <b>Yes</b> to the UAC prompt.</div></div>
<div class="issue"><div class="sym">Log: <code>elevazione rifiutata: continuo senza privilegi</code></div>
<div class="fix"><span class="cause">UAC prompt answered “No”</span>Streaming can still work, but the driver can't be installed. Restart the app and click <b>Yes</b>.</div></div>
<div class="issue"><div class="sym">Log: <code>ERRORE caricamento dll (err=126)</code></div>
<div class="fix"><span class="cause">antivirus quarantined the driver</span>Restore and allow <code>pscapture.dll</code> in your antivirus, then press <b>Install driver</b> again.</div></div>
<div class="issue"><div class="sym">Log: <code>autoloader: copia FALLITA (pldmgr c'e'?)</code></div>
<div class="fix"><span class="cause"><code>pldmgr</code> not installed</span>Harmless: streaming works now. Without the autoloader, press <b>Deploy payload</b> after every console reboot.</div></div>
<div class="issue"><div class="sym">Camera is listed but the image is black</div>
<div class="fix"><span class="cause">stream not running</span>Check the pill. Press <b>Deploy payload</b> and/or <b>START STREAMING</b>.</div></div>
<div class="issue"><div class="sym">Log: <code>stream caduto, riconnetto</code></div>
<div class="fix"><span class="cause">network hiccup or console went to rest mode</span>The app reconnects by itself. If it happens often, use a cable instead of Wi-Fi.</div></div>
<div class="issue"><div class="sym">Colours look wrong (reddish skin, orange sky)</div>
<div class="fix"><span class="cause"><code>gain</code> was changed</span>Set <code>gain</code> back to <code>1.0</code> and press <b>Apply</b>.</div></div>
<div class="issue"><div class="sym">Colours inverted, black bar, picture off-centre</div>
<div class="fix"><span class="cause">alignment</span>Tune <code>shift px</code> using <b>even</b> values only. An odd value swaps the colours.</div></div>
<div class="issue"><div class="sym">Discord shows only 720p</div>
<div class="fix"><span class="cause">Discord's limit, not PSCapture's</span>Discord caps every camera at 720p in calls. Use OBS for 1080p, or Discord screen-share (1080p60 with Nitro).</div></div>
<div class="issue"><div class="sym">OBS sees the camera, Discord doesn't</div>
<div class="fix"><span class="cause">Discord was open during the driver install</span>Quit Discord completely (tray icon too) and reopen it.</div></div>
<div class="issue"><div class="sym">Anything else</div>
<div class="fix"><span class="cause">read the logs</span><code>pscapture-app.log</code> (next to the exe, or in <code>%TEMP%</code>) and the console log <code>/data/pscapture.log</code> over FTP. Then <a href="https://github.com/fstat13/PSCapture/issues">open an issue</a> with both.</div></div>
<div class="noresult" hidden>No match. <a href="https://github.com/fstat13/PSCapture/issues">Open an issue</a> with your <code>pscapture-app.log</code>.</div>
</div>
</section>
<h2 id="trouble">Troubleshooting — symptom → fix</h2>
<table>
<tr><th>Symptom</th><th>Cause → fix</th></tr>
<tr><td>Pill stays <code>AVVIO…</code> / <code>IN ATTESA DELLA PS5</code></td>
<td>Console off or not jailbroken, or wrong IP → turn it on, run the
jailbreak, check the IP field</td></tr>
<tr><td>Log repeats <code>PS5 non raggiungibile</code></td>
<td>Same as above — the app retries by itself</td></tr>
<tr><td>No <b>PS5 Capture</b> camera in Discord/OBS</td>
<td>Driver never registered → press <b>Install driver</b>; the log now
reports the real error</td></tr>
<tr><td><code>ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)</code></td>
<td>App not elevated → restart PSCapture.exe and accept the UAC prompt</td></tr>
<tr><td><code>ERRORE caricando la dll (err=126)</code></td>
<td>Antivirus quarantined the extracted dll → allow
<code>pscapture.dll</code> in the antivirus, then press
<b>Install driver</b> again</td></tr>
<tr><td>Camera listed but image is black</td>
<td>Stream not started → check the pill, press <b>Deploy payload</b>
and/or <b>START STREAMING</b></td></tr>
<tr><td>Colors look wrong</td>
<td><code>gain</code> was moved → set it back to 1.0 and Apply</td></tr>
<tr><td>Image off-center / black bar</td>
<td>Alignment → tune <code>shift px</code> with even numbers</td></tr>
<tr><td>Discord shows 720p</td>
<td>Discord's engine caps every camera at 720p → use OBS for 1080p</td></tr>
<tr><td>OBS sees the camera, Discord doesn't</td>
<td>Discord was open before the driver install → fully quit Discord
(tray included) and reopen</td></tr>
<tr><td>Anything else</td>
<td>Read <code>pscapture-app.log</code> (next to the exe or in
<code>%TEMP%</code>) and the console log
<code>/data/pscapture.log</code> over FTP</td></tr>
</table>
<section id="build" class="reveal">
<div class="sec-head">
<div class="kicker">Build from source</div>
<h2>Two halves, two toolchains.</h2>
<p class="sub">The repository holds sources only; ready-made binaries are on <a href="https://github.com/fstat13/PSCapture/releases">Releases</a>. The full walkthrough (toolchain install, checks, pitfalls) is in <a href="https://github.com/fstat13/PSCapture/blob/main/docs/BUILD.md">BUILD.md</a>.</p>
</div>
<h2 id="build">Build from source</h2>
<div class="build-step">
<div class="num">Step 1 · console</div>
<h3>Payload → <code>pscapture.elf</code></h3>
<p>Needs Docker and the PS5 payload SDK image (prospero-clang; any tag works, here <code>ps5sdk:18</code>).</p>
<div class="codeblock"><pre><span class="p">$ </span>docker run --rm -v "$PWD/payload:/payload" -w /payload ps5sdk:18 make</pre></div>
</div>
<p style="color:var(--dim)">Sources only — the repo contains no binaries.
Two halves, two toolchains. The full walk-through (toolchain install,
verification, gotchas) is in <a href="BUILD.md">docs/BUILD.md</a>.</p>
<h3>1 · Console payload → <code>pscapture.elf</code></h3>
<pre><span class="c"># needs Docker + the PS5 payload SDK image (prospero-clang; here tagged ps5sdk:18)</span>
docker run --rm -v "&lt;repo&gt;/payload:/payload" -w /payload ps5sdk:18 make</pre>
<h3>2 · PC side → <code>pscapture.exe</code> + <code>pscapture.dll</code></h3>
<p style="color:var(--dim)">Needs MinGW-w64 (<code>gcc</code> +
<code>windres</code>). <b>The order is mandatory</b>: the
<code>.res</code> embeds the driver and the payload as they are on disk.</p>
<pre><span class="c"># from the repo root</span>
cp payload/pscapture.elf pc/ <span class="c"># it gets embedded as-is</span>
<div class="build-step">
<div class="num">Step 2 · PC</div>
<h3>App and driver → <code>pscapture.exe</code> + <code>pscapture.dll</code></h3>
<p>Needs MinGW-w64 (<code>gcc</code> + <code>windres</code>). <b>The order matters</b>: the <code>.res</code> embeds the driver and the payload exactly as they are on disk.</p>
<div class="codeblock"><pre><span class="c"># from the repo root</span>
cp payload/pscapture.elf pc/
cd pc
<span class="c"># 1) the capture-card driver (DirectShow source filter)</span>
@@ -280,73 +356,87 @@ windres pscapture.rc -O coff -o pscapture.res
<span class="c"># 3) the single-file app</span>
gcc -O2 -mwindows -o pscapture.exe pscapture.c pscapture.res \
-lws2_32 -lshell32 -ladvapi32 -luxtheme</pre>
-lws2_32 -lshell32 -ladvapi32 -luxtheme</pre></div>
</div>
<details>
<summary>Why the order? (and the two classic build mistakes)</summary>
<p><b>windres embeds files as they are on disk:</b> if the DLL or the
payload is rebuilt after step 2, the exe still carries the old ones.
Rebuild = DLL → windres → exe, every time.<br>
<b><code>-static -static-libgcc</code> on the DLL is mandatory:</b>
without it Windows can fail to load the driver with the cryptic
“the specified module could not be found” (missing mingw runtime).</p>
</details>
<div class="callout info" style="margin-top:22px">
<svg class="i"><use href="#i-info"/></svg>
<p><b>The two classic mistakes.</b> Rebuilding the DLL or the payload <i>after</i> <code>windres</code> leaves the old ones inside the exe, so always go DLL → windres → exe. And <code>-static -static-libgcc</code> on the DLL is mandatory: without it Windows may refuse to load the driver with “the specified module could not be found” (missing MinGW runtime).</p>
</div>
</section>
<h2>Requirements</h2>
<table>
<tr><th>Where</th><th>What</th></tr>
<tr><td>PS5</td><td>Jailbroken (payload loader + FTP + pldmgr autoloader)</td></tr>
<tr><td>PS5</td><td>Latest tested firmware: <b>13.60</b> — see the FAQ for other versions</td></tr>
<tr><td>PC</td><td>Windows 10/11 64-bit, wired network recommended</td></tr>
<tr><td>Network</td><td>Both on the same LAN (gigabit is plenty for 1080p30)</td></tr>
</table>
<section id="faq" class="reveal">
<div class="sec-head">
<div class="kicker">FAQ</div>
<h2>Questions.</h2>
</div>
<div class="faq">
<details><summary>Does it work on firmwares other than 13.60?</summary>
<div class="body"><p>The PC side works with any firmware: the app and the driver only use TCP, FTP and shared memory. The console payload is the version-specific part. It was built and tuned on <b>13.60</b> using display-engine anchors found on that firmware. Other versions may need a small porting pass to find the same anchors. <a href="https://github.com/fstat13/PSCapture/issues">Open an issue</a> with your firmware version.</p></div></details>
<details><summary>Why is the camera 720p in Discord calls?</summary>
<div class="body"><p>That's Discord's voice engine: it requests 1280×720 from every camera. In OBS, PS5 Capture runs at the full 1920×1080. Discord's screen-share goes up to 1080p60 with Nitro.</p></div></details>
<details><summary>Do I need a capture card or Remote Play?</summary>
<div class="body"><p>No. PSCapture never touches Remote Play. The payload reads the display engine on the console itself and sends raw frames over your LAN.</p></div></details>
<details><summary>Does it capture game audio?</summary>
<div class="body"><p>Not yet. PSCapture is video-only for now.</p></div></details>
<details><summary>Does it work on macOS or Linux?</summary>
<div class="body"><p>The PC side is Windows-only, because the camera is a DirectShow device.</p></div></details>
<details><summary>How do I uninstall it?</summary>
<div class="body"><ol>
<li>Close the app.</li>
<li>Run <code>rundll32 "%ProgramFiles%\PSCapture\pscapture.dll",DllUnregisterServer</code> to remove the camera.</li>
<li>Delete <code>C:\Program Files\PSCapture</code>.</li>
<li>Optional, on the console: delete <code>/data/pldmgr/payloads/pscapture/</code> so the autoloader stops starting it.</li>
</ol></div></details>
</div>
</section>
<h2 id="faq">FAQ</h2>
<div class="band reveal">
<div>
<h2>Useful? Leave a star.</h2>
<p>PSCapture is free. A star on GitHub helps other PS5 homebrew users find it.</p>
</div>
<div class="cta">
<a class="btn btn-primary" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Star on GitHub <span class="stars" data-stars hidden></span></a>
<a class="btn btn-ghost" href="https://github.com/fstat13/PSCapture/issues">Report an issue</a>
</div>
</div>
</main>
<details>
<summary>Does it work on firmware versions older than 13.60?</summary>
<p>The PC side works on every firmware: the app and the driver only speak
TCP/FTP and shared memory. The console payload is the version-specific
part — it was built and tuned on <b>13.60</b>, using display-engine
anchors found on that firmware. On other firmwares it may need a small
porting pass (the fix is finding the same anchors on that system).
If you have another firmware, open an issue with your version and we
can add a profile.</p>
</details>
<details>
<summary>Why is the call camera 720p and not 1080p?</summary>
<p>That cap is Discord's voice engine, not PSCapture: it requests 1280x720
from every camera. For full-quality capture use OBS (where PS5 Capture
runs at 1920x1080) or Discord's screen-share, which goes up to
1080p60 with Nitro.</p>
</details>
<details>
<summary>Is a capture card or Remote Play needed?</summary>
<p>No. PSCapture never touches Remote Play: the payload reads the display
engine on the console itself and streams raw frames over your LAN.</p>
</details>
<details>
<summary>How do I uninstall it?</summary>
<p>Close the app, then <code>rundll32 "%ProgramFiles%\PSCapture\pscapture.dll",DllUnregisterServer</code>,
delete <code>C:\Program Files\PSCapture</code>, and optionally delete
<code>/data/pldmgr/payloads/pscapture/</code> on the console so the
autoloader stops starting the payload.</p>
</details>
<footer>
PSCapture — bring your PS5 screen anywhere on your LAN, no card, no cloud.
<br><b>by fstat13</b> &middot; source-available. Built for the homebrew community. <span class="ok">Play responsibly.</span>
<br><br>
Docs: <a href="GUIDE.md">fool-proof guide (md)</a> &middot;
<a href="BUILD.md">build from source (md)</a> &middot;
<a href="INSTALLER.md">what the installer does inside</a> &middot;
<a href="REVERSE_ENGINEERING.md">reverse engineering</a> &middot;
<a href="ENGINEERING_LOG.md">engineering log</a>
</footer>
</div>
<footer>
<div class="wrap">
<div class="foot">
<div>
<a class="logo" href="#top"><img src="pscapture.png" alt="">PSCapture</a>
<p>Your PS5 screen anywhere on your LAN. No card, no cloud.<br>by <b>fstat13</b></p>
</div>
<div>
<h4>Guides</h4>
<ul>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/GUIDE.md">Step-by-step guide</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/BUILD.md">Build from source</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/INSTALLER.md">What the installer does</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/REVERSE_ENGINEERING.md">Reverse engineering</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/ENGINEERING_LOG.md">Engineering log</a></li>
</ul>
</div>
<div>
<h4>Project</h4>
<ul>
<li><a href="https://github.com/fstat13/PSCapture">GitHub</a></li>
<li><a href="https://github.com/fstat13/PSCapture/releases">Releases</a></li>
<li><a href="https://github.com/fstat13/PSCapture/issues">Issues</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/LICENSE">License</a></li>
</ul>
</div>
</div>
<div class="legal">
<span>© 2026 fstat13 · source-available</span>
<span>Not affiliated with Sony Interactive Entertainment. “PlayStation” and “PS5” are trademarks of their respective owners.</span>
</div>
</div>
</footer>
<script src="main.js"></script>
</body>
</html>
+398 -313
View File
@@ -1,358 +1,443 @@
<!DOCTYPE html>
<html lang="it">
<html lang="it" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PSCapture di fstat13 — la PS5 come scheda di acquisizione sul PC</title>
<style>
:root{
--bg:#0d1015; --card:#151a22; --card2:#1b222d; --line:#242c38;
--fg:#e8edf4; --dim:#96a0b0; --accent:#0070d1; --accent2:#35a3ff;
--rec:#e22236; --ok:#1fbf6b;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);
font:16px/1.65 "Segoe UI",system-ui,-apple-system,Roboto,sans-serif;}
a{color:var(--accent2);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:880px;margin:0 auto;padding:0 20px}
header{background:
radial-gradient(900px 320px at 20% -40%, rgba(0,112,209,.35), transparent 60%),
radial-gradient(700px 300px at 90% -30%, rgba(226,34,54,.22), transparent 60%),
var(--bg);
border-bottom:1px solid var(--line);padding:56px 0 40px}
.brand{display:flex;align-items:center;gap:18px}
.brand img{width:84px;height:84px;border-radius:20px;box-shadow:0 8px 30px rgba(0,0,0,.5)}
h1{font-size:44px;margin:0;letter-spacing:.5px}
.tag{color:var(--dim);font-size:18px;margin-top:6px}
.badges{margin-top:22px;display:flex;gap:10px;flex-wrap:wrap}
.badge{border:1px solid var(--line);background:var(--card);
padding:5px 12px;border-radius:999px;font-size:13px;color:var(--dim)}
.badge b{color:var(--fg)}
h2{margin:52px 0 14px;font-size:26px}
h3{margin:26px 0 10px;font-size:19px}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px 20px}
.card h3{margin:0 0 8px;font-size:17px}
.card p{margin:0;color:var(--dim);font-size:14.5px}
.flow{display:flex;gap:10px;align-items:stretch;flex-wrap:wrap;margin-top:16px}
.box{flex:1;min-width:180px;background:var(--card2);border:1px solid var(--line);
border-radius:12px;padding:14px 16px}
.box b{display:block;margin-bottom:6px}
.box div{color:var(--dim);font-size:13.5px}
.arrow{align-self:center;color:var(--accent2);font-size:22px;padding:0 2px}
ol.steps{counter-reset:s;list-style:none;padding:0;margin:18px 0 0}
ol.steps li{counter-increment:s;position:relative;padding:0 0 18px 54px}
ol.steps li::before{content:counter(s);position:absolute;left:0;top:0;
width:34px;height:34px;border-radius:10px;background:var(--accent);
color:#fff;font-weight:600;display:flex;align-items:center;justify-content:center}
ol.steps b{color:var(--fg)}
ol.steps span{color:var(--dim)}
ul.plain{color:var(--dim);padding-left:22px}
ul.plain b{color:var(--fg)}
table{width:100%;border-collapse:collapse;margin-top:14px;font-size:14.5px}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);
vertical-align:top}
th{color:var(--dim);font-weight:500}
td code,th code{white-space:nowrap}
code,kbd{background:#0a0d12;border:1px solid var(--line);border-radius:6px;
padding:1px 7px;font:13.5px/1.5 Consolas,monospace;color:#cfe3ff}
pre{background:#0a0d12;border:1px solid var(--line);border-radius:12px;
padding:14px 16px;overflow-x:auto;font:13.5px/1.6 Consolas,monospace;
color:#cfe3ff}
pre .c{color:#5f7186}
details{background:var(--card);border:1px solid var(--line);border-radius:12px;
padding:14px 18px;margin:10px 0}
summary{cursor:pointer;font-weight:600}
details p{color:var(--dim);margin:10px 0 0}
.note{background:linear-gradient(0deg,rgba(0,112,209,.08),rgba(0,112,209,.08)),
var(--card);border:1px solid #23405f;border-radius:12px;padding:14px 18px;color:#cfe3ff}
.warn{background:linear-gradient(0deg,rgba(226,34,54,.07),rgba(226,34,54,.07)),
var(--card);border:1px solid #5f2330;border-radius:12px;padding:14px 18px;color:#f3cdd3}
footer{color:var(--dim);border-top:1px solid var(--line);margin-top:70px;
padding:26px 0 40px;font-size:13.5px}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;
background:var(--rec);margin-right:8px;vertical-align:1px}
.ok{color:var(--ok);font-weight:600}
.toc{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
.toc a{border:1px solid var(--line);background:var(--card);border-radius:999px;
padding:4px 12px;font-size:13px}
.lang{position:absolute;top:18px;right:22px;font-size:13.5px}
</style>
<title>PSCapture — la PS5 come scheda di acquisizione sul PC</title>
<meta name="description" content="PSCapture trasforma una PS5 jailbroken in una scheda di acquisizione per il PC: lo schermo della console passa sulla tua rete locale e compare in Discord, OBS e ffmpeg come la webcam “PS5 Capture”.">
<meta name="theme-color" content="#07090d">
<link rel="icon" type="image/png" href="pscapture.png">
<link rel="alternate" hreflang="en" href="https://fstat13.github.io/PSCapture/">
<link rel="alternate" hreflang="it" href="https://fstat13.github.io/PSCapture/index.it.html">
<meta property="og:type" content="website">
<meta property="og:locale" content="it_IT">
<meta property="og:title" content="PSCapture — lo schermo della PS5, live sul tuo PC">
<meta property="og:description" content="Lo schermo della PS5 compare in Discord, OBS e ffmpeg come la webcam “PS5 Capture”. Niente capture card, niente Remote Play, niente cloud.">
<meta property="og:image" content="https://fstat13.github.io/PSCapture/pscapture.png">
<meta property="og:url" content="https://fstat13.github.io/PSCapture/index.it.html">
<meta name="twitter:card" content="summary">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header style="position:relative">
<div class="lang">🇬🇧 <a href="index.html">English</a></div>
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="i-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
<symbol id="i-star" viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></symbol>
<symbol id="i-video" viewBox="0 0 24 24"><path d="m22 8-6 4 6 4V8Z"/><rect x="2" y="6" width="14" height="12" rx="2"/></symbol>
<symbol id="i-zap" viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></symbol>
<symbol id="i-file" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
<symbol id="i-console" viewBox="0 0 24 24"><rect x="2" y="6" width="20" height="12" rx="3"/><line x1="6" y1="12" x2="10" y2="12"/><line x1="8" y1="10" x2="8" y2="14"/><circle cx="16" cy="11" r=".6"/><circle cx="18" cy="13" r=".6"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></symbol>
<symbol id="i-alert" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
</svg>
<nav class="nav">
<div class="wrap">
<div class="brand">
<img src="pscapture.png" alt="PSCapture">
<div>
<h1>PSCapture</h1>
<div class="tag">Lo schermo della PlayStation&nbsp;5, live sul tuo PC — come una vera scheda di acquisizione.</div>
<a class="logo" href="#top"><img src="pscapture.png" alt="">PSCapture</a>
<div class="nav-links">
<a href="#how">Come funziona</a>
<a href="#start">Installazione</a>
<a href="#trouble">Problemi</a>
<a href="#build">Build</a>
<a href="#faq">FAQ</a>
</div>
<div class="nav-right">
<a class="lang" href="index.html" hreflang="en">English</a>
<a class="btn btn-ghost btn-sm" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Star <span class="stars" data-stars hidden></span></a>
</div>
</div>
</nav>
<header class="hero" id="top">
<div class="wrap hero-grid">
<div>
<span class="eyebrow"><span class="tagv">v0.1</span>Testato sul firmware PS5 13.60</span>
<h1>Lo schermo della PS5,<br><span class="grad">live sul tuo PC.</span></h1>
<p class="lead">PSCapture trasforma una PlayStation&nbsp;5 jailbroken in una scheda di acquisizione.
La console manda il proprio schermo sulla tua rete locale e Windows lo vede come una
webcam chiamata <b>PS5 Capture</b>, pronta per Discord, OBS e ffmpeg.</p>
<div class="cta">
<a class="btn btn-primary" href="https://github.com/fstat13/PSCapture/releases"><svg class="i"><use href="#i-download"/></svg>Scarica per Windows</a>
<a class="btn btn-ghost" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Metti una stella <span class="stars" data-stars hidden></span></a>
</div>
<div class="hero-meta">
<span><svg class="i"><use href="#i-check"/></svg>Niente capture card</span>
<span><svg class="i"><use href="#i-check"/></svg>Niente Remote Play</span>
<span><svg class="i"><use href="#i-check"/></svg>Niente cloud</span>
<span><svg class="i"><use href="#i-check"/></svg>Un solo .exe</span>
</div>
</div>
<div class="badges">
<span class="badge"><b>PS5</b> jailbroken</span>
<span class="badge"><b>Windows</b> 10 / 11</span>
<span class="badge"><span class="dot"></span><b>Niente</b> capture card</span>
<span class="badge"><b>Niente</b> Remote&nbsp;Play</span>
<span class="badge">1080p &middot; ~30 fps</span>
<div class="app" role="img" aria-label="La finestra di PSCapture: il log mostra il controllo del driver, l'invio del payload e la connessione dello stream; la pillola di stato dice STREAMING.">
<div class="app-bar">
<img src="pscapture.png" alt="">
<span class="t">PSCapture by fstat13 - PS5 screen to PC</span>
<span class="w"><span>–</span><span>□</span><span>✕</span></span>
</div>
<div class="app-body">
<div class="app-head">
<div class="app-title">PSCapture<small>PS5 screen to PC</small></div>
<span class="pill live"><i></i> STREAMING 29.8 fps</span>
</div>
<div class="app-row">
<span class="app-lbl">PS5 IP</span><span class="app-in">192.168.0.36</span>
<span class="app-btn">Deploy payload</span><span class="app-btn">Install driver</span>
</div>
<div class="app-row">
<span class="app-btn big">START STREAMING</span><span class="app-btn stop">STOP</span>
<span class="app-knobs app-row" style="margin:0">
<span class="app-lbl">gain</span><span class="app-in">1.0</span>
<span class="app-lbl">shift px</span><span class="app-in">0</span>
</span>
</div>
<div class="app-log" aria-hidden="true">
<div>PSCapture pronto: premo Start e faccio tutto da solo.</div>
<div>--- AVVIO AUTOMATICO ---</div>
<div>driver gia' installato</div>
<div>payload dall'eseguibile (175480 byte)</div>
<div>payload 175480 byte, invio a 192.168.0.36</div>
<div>autoloader aggiornato (pldmgr)</div>
<div class="hl">payload avviato - la PS5 deve mostrare la notifica PSCapture</div>
<div>porta stream: 5599</div>
<div class="ok">collegato a 192.168.0.36:5599</div>
</div>
</div>
</div>
<div class="toc">
<a href="#start">Avvio rapido</a>
<a href="#daily">Uso quotidiano</a>
<a href="#trouble">Problemi</a>
<a href="#build">Build dai sorgenti</a>
<a href="#faq">FAQ</a>
</div>
<div class="wrap">
<div class="stats">
<div class="stat"><div class="n">1920×1080</div><div class="l">full HD in OBS</div></div>
<div class="stat"><div class="n">~30 fps</div><div class="l">su una normale LAN</div></div>
<div class="stat"><div class="n">0</div><div class="l">ricodifiche sulla console</div></div>
<div class="stat"><div class="n">1 file</div><div class="l">driver e payload inclusi</div></div>
</div>
</div>
</header>
<div class="wrap">
<main class="wrap">
<h2>Cos'è</h2>
<div class="cards">
<div class="card">
<h3>🎥 Scheda di acquisizione virtuale</h3>
<p>PSCapture registra un dispositivo DirectShow chiamato <b>PS5 Capture</b>.
Discord, OBS e ffmpeg lo vedono come un'Elgato — scegli la camera e vai
live.</p>
<section id="why" class="reveal">
<div class="sec-head">
<div class="kicker">Perché PSCapture</div>
<h2>Una capture card, senza la capture card.</h2>
<p class="sub">Funziona tutto con quello che hai già: la console, la tua rete e il tuo PC.</p>
</div>
<div class="card">
<h3>⚡ Direttamente dalla GPU</h3>
<p>Il payload sulla console legge direttamente il buffer di scan-out del
display engine e manda i frame NV12 sulla tua LAN. Nessuna
ricodifica, nessun cloud.</p>
<div class="grid g3">
<div class="card"><div class="ico"><svg class="i"><use href="#i-video"/></svg></div>
<h3>Una vera webcam</h3>
<p>Un filtro sorgente DirectShow chiamato <b>PS5 Capture</b>. Qualsiasi programma che apre una webcam può aprire la tua console.</p></div>
<div class="card"><div class="ico v"><svg class="i"><use href="#i-zap"/></svg></div>
<h3>Direttamente dal display engine</h3>
<p>Il payload legge il buffer di scan-out della console e manda frame NV12 grezzi. Niente ricodifica, quindi niente attese per un encoder.</p></div>
<div class="card"><div class="ico g"><svg class="i"><use href="#i-file"/></svg></div>
<h3>Un file, una finestra</h3>
<p><b>PSCapture.exe</b> installa il driver, manda il payload, lo registra nell'autoloader e si collega. Tutto da solo.</p></div>
<div class="card"><div class="ico a"><svg class="i"><use href="#i-refresh"/></svg></div>
<h3>Si riprende da solo</h3>
<p>Console ancora spenta? Riprova ogni 2 secondi. Stream caduto? Si ricollega. Non devi stargli dietro.</p></div>
<div class="card"><div class="ico"><svg class="i"><use href="#i-sliders"/></svg></div>
<h3>Regolazioni in diretta</h3>
<p><code>gain</code> del colore e <code>shift</code> orizzontale si applicano in circa un secondo e restano salvati in <code>pscapture.ini</code>.</p></div>
<div class="card"><div class="ico v"><svg class="i"><use href="#i-lock"/></svg></div>
<h3>Resta nella tua rete</h3>
<p>Niente account, niente server esterni, niente Remote Play. I frame vanno dalla console al PC e basta.</p></div>
</div>
<div class="card">
<h3>🖱️ Un solo eseguibile</h3>
<p><b>PSCapture.exe</b> contiene già il payload per la console:
installa il driver, manda il payload, avvia lo stream — una finestra,
un click.</p>
</section>
<section id="how" class="reveal">
<div class="sec-head">
<div class="kicker">Come funziona</div>
<h2>Tre pezzi piccoli, una webcam.</h2>
<p class="sub">Un payload sulla console, un feeder e un driver sul PC. I programmi che usi già vedono semplicemente una webcam.</p>
</div>
</div>
<div class="pipe">
<div class="node"><div class="where">PS5</div><h3>Payload</h3>
<p>Legge il ring di scan-out del display engine, costruisce i frame NV12 e li serve sulla porta TCP <code>5599</code>. Mostra la notifica <i>“PSCapture is streaming on:5599”</i>.</p></div>
<div class="link"><span>TCP · NV12<br>rete locale</span><div class="line"></div></div>
<div class="node"><div class="where">PC · PSCapture.exe</div><h3>Feeder</h3>
<p>Riceve i frame e li pubblica in un ring di memoria condivisa.</p></div>
<div class="link"><span>memoria<br>condivisa</span><div class="line"></div></div>
<div class="node"><div class="where">PC · pscapture.dll</div><h3>Driver</h3>
<p>Un filtro sorgente DirectShow che espone il ring come la webcam <b>PS5 Capture</b>.</p></div>
<div class="link"><span>DirectShow</span><div class="line"></div></div>
<div class="node end"><div class="where">I tuoi programmi</div><h3>Qualsiasi app video</h3>
<p>Scegli <b>PS5 Capture</b> come una webcam qualunque.</p>
<div class="chips"><span class="chip">Discord</span><span class="chip">OBS</span><span class="chip">ffmpeg</span></div></div>
</div>
</section>
<h2>Come funziona</h2>
<div class="flow">
<div class="box"><b>PS5 &middot; payload</b>
<div>legge il ring di scan-out del display engine, costruisce i frame
NV12, li serve su una porta TCP e mostra il toast:
<i>“PSCapture is streaming on:PORT”</i>.</div></div>
<div class="arrow">→</div>
<div class="box"><b>PC &middot; feeder</b>
<div>riceve i frame e li pubblica in un ring di memoria condivisa.</div></div>
<div class="arrow">→</div>
<div class="box"><b>PC &middot; driver</b>
<div>il filtro DirectShow espone il ring come una camera per tutti i
programmi.</div></div>
</div>
<section id="requirements" class="reveal">
<div class="sec-head">
<div class="kicker">Prima di iniziare</div>
<h2>Cosa ti serve.</h2>
</div>
<div class="grid g2">
<div class="req">
<div class="req-h"><div class="ico"><svg class="i"><use href="#i-console"/></svg></div><h3>Sulla PS5</h3></div>
<ul>
<li><svg class="i"><use href="#i-check"/></svg><span>Una console jailbroken<small>Sviluppato e tarato sul firmware <b>13.60</b>. Per le altre versioni vedi la FAQ.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Payload loader sulla porta TCP <code>9021</code><small>Tipo <code>elfldr</code>, come in tutti i setup di jailbreak attuali.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>FTP anonimo sulla porta TCP <code>2121</code><small>L'app legge e scrive alcuni file sulla console.</small></span></li>
<li class="opt"><svg class="i"><use href="#i-check"/></svg><span>Autoloader <code>pldmgr</code> <span class="tagopt">opzionale</span><small>Avvia il payload a ogni accensione, così non devi mai mandarlo a mano.</small></span></li>
</ul>
</div>
<div class="req">
<div class="req-h"><div class="ico v"><svg class="i"><use href="#i-monitor"/></svg></div><h3>Sul PC</h3></div>
<ul>
<li><svg class="i"><use href="#i-check"/></svg><span>Windows 10 o 11, 64 bit<small>La webcam è un dispositivo DirectShow, quindi il lato PC è solo per Windows.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Stessa rete della console<small>Meglio via cavo. Il gigabit basta e avanza per 1080p a 30 fps.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span>Permessi di amministratore, una volta<small>Per registrare la webcam per tutti i programmi del PC.</small></span></li>
<li><svg class="i"><use href="#i-check"/></svg><span><b>PSCapture.exe</b> dalle <a href="https://github.com/fstat13/PSCapture/releases">Releases</a><small>Un unico file: payload e driver sono già dentro.</small></span></li>
</ul>
</div>
</div>
</section>
<h2 id="start">Avvio rapido — passo per passo</h2>
<section id="start" class="reveal">
<div class="sec-head">
<div class="kicker">Installazione</div>
<h2>Da zero a live in cinque passi.</h2>
<p class="sub">Dal passo 2 in poi ti basta guardare: l'app fa il resto e ti dice cosa sta facendo.</p>
</div>
<ol class="steps">
<li><h3>Fai il jailbreak della console</h3>
<p>Avvia il tuo solito jailbreak, così il payload loader (<code>9021</code>) e l'FTP (<code>2121</code>) sono attivi.</p></li>
<li><h3>Doppio clic su <code>PSCapture.exe</code></h3>
<p>Windows chiede <i>“Consentire a questa app di apportare modifiche al dispositivo?”</i>. Clicca <b>Sì</b>. L'app chiede da sola i permessi, non serve il tasto destro.</p></li>
<li><h3>Controlla l'IP</h3>
<p>Quello predefinito è <code>192.168.0.36</code>. Se la tua PS5 ha un altro indirizzo, scrivilo in <b>PS5 IP</b> e premi <b>Apply</b>: viene ricordato. Lo trovi sulla console in <i>Impostazioni → Rete → Stato della connessione → Visualizza stato della connessione</i>.</p></li>
<li><h3>Non toccare niente</h3>
<p>Al primo avvio l'app installa il driver, poi manda il payload e si collega. Sulla console compare <i>“PSCapture is streaming on:5599”</i> e la pillola di stato diventa <b style="color:var(--green)">verde: STREAMING</b>.</p></li>
<li><h3>Scegli <b>PS5 Capture</b> nel tuo programma</h3>
<p>Discord, OBS o ffmpeg. Più sotto trovi <a href="#use">dove trovarla</a>.</p></li>
</ol>
<div class="note" style="margin-bottom:20px">
<b>Cosa serve prima.</b> Una PS5 jailbroken con il setup standard:
un payload loader sulla porta TCP <code>9021</code> (tipo
<code>elfldr</code>), FTP anonimo sulla <code>2121</code>, e opzionalmente
l'autoloader <code>pldmgr</code> così il payload parte a ogni boot.
PC e console sulla stessa LAN. Scarica <b>PSCapture.exe</b> dalla pagina
Releases — <b>è un unico file</b>: il payload per la console e il driver
per Windows sono dentro di lui.
</div>
<h3 style="margin:30px 0 6px">Come leggere il log</h3>
<p style="color:var(--dim)">Queste sono le righe che vedrai, nell'ordine in cui compaiono.</p>
<div class="term" style="margin-top:14px">
<div class="term-bar"><i></i><i></i><i></i><b>pscapture-app.log</b></div>
<div class="term-row"><code>driver gia' installato</code><span class="m">La webcam è già registrata. Niente da fare.</span></div>
<div class="term-row"><code>driver non installato: lo installo ora (primo avvio)<br>driver estratto dall'eseguibile (… byte)<br>driver registrato - la webcam 'PS5 Capture' e' pronta</code><span class="m">Primo avvio: il driver viene estratto dall'exe e registrato.</span></div>
<div class="term-row"><code>payload dall'eseguibile (… byte)<br>payload … byte, invio a 192.168.0.36</code><span class="m">Il payload sta andando verso la console.</span></div>
<div class="term-row"><code>autoloader aggiornato (pldmgr)</code><span class="m">Il payload è stato copiato nell'autoloader: partirà a ogni accensione.</span></div>
<div class="term-row"><code>payload avviato - la PS5 deve mostrare la notifica PSCapture</code><span class="m">Il payload gira. Cerca la notifica sulla TV.</span></div>
<div class="term-row ok"><code>porta stream: 5599<br>collegato a 192.168.0.36:5599</code><span class="m">Collegato. La pillola dice <b>STREAMING</b> con gli fps in tempo reale.</span></div>
</div>
<ol class="steps">
<li><b>Fai il jailbreak della console</b><br>
<span>Qualsiasi jailbreak kernel-capable che ti dia il payload loader
(:9021), l'FTP anonimo (:2121) e — opzionale — l'autoloader
<code>pldmgr</code>.</span></li>
<li><b>Doppio click su <code>PSCapture.exe</code></b><br>
<span>Windows chiede <i>“Consenti a questa app di apportare modifiche?”</i>
→ <b>Sì</b>. L'app si eleva da sola; i privilegi servono una volta, per
registrare la webcam virtuale per tutti i programmi.</span></li>
<li><b>Controlla l'IP</b><br>
<span>Se la tua PS5 non è a <code>192.168.0.36</code>, scrivi il suo
indirizzo nel campo <b>PS5 IP</b> e premi <b>Apply</b>.</span></li>
<li><b>Guarda e non premere niente</b><br>
<span>L'app fa tutto da sola, e te lo racconta nel pannello log scuro
(tabella qui sotto). Sulla console vedrai anche la notifica di sistema
<i>“PSCapture is streaming on:PORT”</i>.</span></li>
<li><b>Usalo in qualsiasi programma</b><br>
<span>Discord (in chiamata): <i>Impostazioni → Voce e video →
Telecamera</i> → <b>PS5 Capture</b> (720p — Discord chiede 720p a tutte
le camere, capture card incluse). OBS: aggiungi un <i>Video Capture
Device</i> → <b>1920x1080</b> pieno. ffmpeg:
<code>-f dshow -i video="PS5 Capture"</code>.</span></li>
</ol>
<div class="callout warn" style="margin-top:18px">
<svg class="i"><use href="#i-alert"/></svg>
<p><b>Pillola ferma su “IN ATTESA DELLA PS5”?</b> La console è spenta, il jailbreak non è ancora partito o l'IP è sbagliato. Non serve riavviare niente: l'app riprova ogni 2 secondi e si collega appena le porte <code>9021</code> e <code>2121</code> rispondono.</p>
</div>
</section>
<h3>Cosa devi vedere nel log dell'app</h3>
<table>
<tr><th>Riga</th><th>Significato</th></tr>
<tr><td><code>driver gia' installato</code></td>
<td>camera già registrata — niente da fare</td></tr>
<tr><td><code>driver estratto dall'eseguibile (… byte)</code><br>
<code>driver registrato - la webcam 'PS5 Capture' e' pronta</code></td>
<td>primo avvio: il driver è stato estratto dall'exe e registrato</td></tr>
<tr><td><code>payload dall'eseguibile (… byte)</code></td>
<td>il payload sta viaggiando verso la PS5</td></tr>
<tr><td><code>payload avviato - la PS5 deve mostrare la notifica PSCapture</code></td>
<td>il payload sta girando sulla console</td></tr>
<tr><td><code>porta stream: 9xxx</code> → <code>collegato a …:9xxx</code></td>
<td>stream connesso — la pillola diventa <span class="ok">verde: STREAMING</span></td></tr>
</table>
<section id="use" class="reveal">
<div class="sec-head">
<div class="kicker">Usalo</div>
<h2>Funziona ovunque funzioni una webcam.</h2>
</div>
<div class="grid g3">
<div class="card target"><h3>Discord</h3>
<div class="path"><i>Impostazioni → Voce e video → Videocamera</i> → <b>PS5 Capture</b></div>
<p style="margin-top:10px">Discord chiede 720p a tutte le webcam, capture card comprese. Per il 1080p60 usa la condivisione schermo (Nitro).</p>
<div class="res">in chiamata <b>1280×720</b></div></div>
<div class="card target"><h3>OBS Studio</h3>
<div class="path"><i>Fonti → + → Dispositivo di acquisizione video</i> → <b>PS5 Capture</b></div>
<p style="margin-top:10px">Risoluzione piena, pronta per registrare o andare in live.</p>
<div class="res">nativo <b>1920×1080</b></div></div>
<div class="card target"><h3>ffmpeg</h3>
<div class="path"><code>ffmpeg -f dshow -i video="PS5 Capture" capture.mkv</code></div>
<p style="margin-top:10px">Per catture da script e test veloci.</p>
<div class="res">nativo <b>1920×1080</b></div></div>
</div>
</section>
<div class="warn" style="margin-top:18px">
<b>Pillola ferma su “IN ATTESA DELLA PS5 — accesa e col jailbreak avviato?”</b><br>
La console è spenta, non ancora jailbroken, o l'IP è sbagliato. L'app
riprova ogni 2 secondi: nel momento in cui la console è pronta si connette
da sola. Porte da controllare sulla console: <code>9021</code> e
<code>2121</code> devono essere aperte.
</div>
<section id="daily" class="reveal">
<div class="sec-head">
<div class="kicker">Uso quotidiano</div>
<h2>Aprilo e gioca.</h2>
</div>
<div class="grid g3">
<div class="card knob"><span class="k">ogni giorno</span>
<p>Apri <b>PSCapture.exe</b> e accetta il prompt UAC. Con l'autoloader lo stream parte da solo.</p>
<ul>
<li><b>Deploy payload</b>: rimanda il payload a mano, dopo un riavvio senza autoloader.</li>
<li><b>Install driver</b>: registra di nuovo la webcam se il log mostra un errore del driver.</li>
<li><b>STOP</b> o chiudere l'app: la webcam si ferma sull'ultimo frame e resta nell'elenco.</li>
</ul></div>
<div class="card knob"><span class="k">gain</span>
<p>Intensità del colore (croma).</p>
<ul><li><code>1.0</code> è il valore grezzo e corretto.</li><li>Cambialo solo se i colori sembrano sbagliati.</li></ul></div>
<div class="card knob"><span class="k">shift px</span>
<p>Sposta l'immagine a destra o a sinistra.</p>
<ul><li><b>Solo numeri pari</b>: un valore dispari scambia i colori.</li><li>Le scritte dei titoli della console sono un buon riferimento.</li></ul></div>
</div>
</section>
<h2 id="daily">Uso quotidiano e regolazioni</h2>
<ul class="plain">
<li>Apri <b>PSCapture.exe</b> (UAC → Sì). Se il payload è già sulla console
(autoloader) lo stream parte da solo; altrimenti premi
<b>Deploy payload</b> una volta.</li>
<li><b>Stop</b> / chiudere l'app ferma lo stream — la camera si congela
sull'ultimo frame e gli altri programmi continuano a vedere il
dispositivo.</li>
<li><b>gain</b> — forza del colore. <b>Lascia 1.0</b> (grezzo) a meno che i
colori non sembrino sbagliati.</li>
<li><b>shift px</b> — allineamento orizzontale. <b>Solo numeri PARI</b>
(un valore dispari inverte i colori). Si applica in diretta ed è
salvato in <code>pscapture.ini</code> accanto all'exe.</li>
</ul>
<section id="trouble" class="reveal">
<div class="sec-head">
<div class="kicker">Problemi</div>
<h2>Sintomo → rimedio.</h2>
<p class="sub">Scrivi cosa vedi, oppure incolla una riga del log.</p>
</div>
<div class="search"><svg class="i"><use href="#i-search"/></svg>
<input id="q" type="search" placeholder="es. nera, 720p, err=126, ATTESA…" autocomplete="off" aria-label="Filtra i problemi"></div>
<div class="issues">
<div class="issue"><div class="sym">La pillola resta su <code>AVVIO…</code> o <code>IN ATTESA DELLA PS5</code></div>
<div class="fix"><span class="cause">console spenta, senza jailbreak o IP sbagliato</span>Accendi la console, avvia il jailbreak (le porte 9021 e 2121 devono essere attive), controlla il campo <b>PS5 IP</b>. L'app continua a riprovare.</div></div>
<div class="issue"><div class="sym">Il log ripete <code>PS5 non raggiungibile (…), riprovo</code></div>
<div class="fix"><span class="cause">console non raggiungibile</span>Come sopra. Si collega da sola appena la console risponde.</div></div>
<div class="issue"><div class="sym">Log: <code>elfldr :9021 non raggiungibile</code></div>
<div class="fix"><span class="cause">payload loader non attivo</span>Rilancia il jailbreak, poi premi <b>Deploy payload</b>.</div></div>
<div class="issue"><div class="sym">La webcam <b>PS5 Capture</b> non compare in Discord o OBS</div>
<div class="fix"><span class="cause">driver non registrato</span>Premi <b>Install driver</b> e leggi la riga successiva del log: riporta l'errore vero.</div></div>
<div class="issue"><div class="sym">Log: <code>ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)</code></div>
<div class="fix"><span class="cause">app senza permessi di amministratore</span>Chiudila, riapri <code>PSCapture.exe</code> e rispondi <b>Sì</b> al prompt UAC.</div></div>
<div class="issue"><div class="sym">Log: <code>elevazione rifiutata: continuo senza privilegi</code></div>
<div class="fix"><span class="cause">al prompt UAC hai risposto “No”</span>Lo stream può funzionare lo stesso, ma il driver non si installa. Riavvia l'app e clicca <b>Sì</b>.</div></div>
<div class="issue"><div class="sym">Log: <code>ERRORE caricamento dll (err=126)</code></div>
<div class="fix"><span class="cause">l'antivirus ha messo in quarantena il driver</span>Ripristina e consenti <code>pscapture.dll</code> nell'antivirus, poi premi di nuovo <b>Install driver</b>.</div></div>
<div class="issue"><div class="sym">Log: <code>autoloader: copia FALLITA (pldmgr c'e'?)</code></div>
<div class="fix"><span class="cause"><code>pldmgr</code> non installato</span>Nessun danno: lo stream funziona. Senza autoloader, però, dopo ogni riavvio della console devi premere <b>Deploy payload</b>.</div></div>
<div class="issue"><div class="sym">La webcam compare ma l'immagine è nera</div>
<div class="fix"><span class="cause">stream non partito</span>Guarda la pillola. Premi <b>Deploy payload</b> e/o <b>START STREAMING</b>.</div></div>
<div class="issue"><div class="sym">Log: <code>stream caduto, riconnetto</code></div>
<div class="fix"><span class="cause">problema di rete o console in modalità riposo</span>L'app si ricollega da sola. Se succede spesso, passa dal Wi-Fi al cavo.</div></div>
<div class="issue"><div class="sym">I colori sembrano sbagliati (pelle rossastra, cielo arancione)</div>
<div class="fix"><span class="cause">hai cambiato <code>gain</code></span>Rimetti <code>gain</code> a <code>1.0</code> e premi <b>Apply</b>.</div></div>
<div class="issue"><div class="sym">Colori invertiti, barra nera, immagine decentrata</div>
<div class="fix"><span class="cause">allineamento</span>Regola <code>shift px</code> usando solo valori <b>pari</b>. Un valore dispari scambia i colori.</div></div>
<div class="issue"><div class="sym">Discord mostra solo 720p</div>
<div class="fix"><span class="cause">limite di Discord, non di PSCapture</span>In chiamata Discord limita ogni webcam a 720p. Usa OBS per il 1080p, o la condivisione schermo di Discord (1080p60 con Nitro).</div></div>
<div class="issue"><div class="sym">OBS vede la webcam, Discord no</div>
<div class="fix"><span class="cause">Discord era aperto durante l'installazione del driver</span>Chiudi Discord del tutto (anche dall'icona nella tray) e riaprilo.</div></div>
<div class="issue"><div class="sym">Tutto il resto</div>
<div class="fix"><span class="cause">leggi i log</span><code>pscapture-app.log</code> (accanto all'exe o in <code>%TEMP%</code>) e il log della console <code>/data/pscapture.log</code> via FTP. Poi <a href="https://github.com/fstat13/PSCapture/issues">apri una issue</a> allegandoli.</div></div>
<div class="noresult" hidden>Nessun risultato. <a href="https://github.com/fstat13/PSCapture/issues">Apri una issue</a> con il tuo <code>pscapture-app.log</code>.</div>
</div>
</section>
<h2 id="trouble">Problemi — sintomo → rimedio</h2>
<table>
<tr><th>Sintomo</th><th>Causa → rimedio</th></tr>
<tr><td>La pillola resta su <code>AVVIO…</code> / <code>IN ATTESA DELLA PS5</code></td>
<td>Console spenta o non jailbroken, o IP sbagliato → accendila, avvia
il jailbreak, controlla il campo IP</td></tr>
<tr><td>Il log ripete <code>PS5 non raggiungibile</code></td>
<td>Come sopra — l'app riprova da sola</td></tr>
<tr><td>La camera <b>PS5 Capture</b> non appare in Discord/OBS</td>
<td>Driver mai registrato → premi <b>Install driver</b>; il log ora
riporta l'errore vero</td></tr>
<tr><td><code>ERRORE: driver non scrivibile in C:\Program Files\PSCapture (serve admin?)</code></td>
<td>App non elevata → riavvia PSCapture.exe e accetta il prompt UAC</td></tr>
<tr><td><code>ERRORE caricando la dll (err=126)</code></td>
<td>L'antivirus ha messo in quarantena la dll estratta → sblocca
<code>pscapture.dll</code> nell'antivirus, poi premi di nuovo
<b>Install driver</b></td></tr>
<tr><td>La camera appare ma l'immagine è nera</td>
<td>Stream non partito → controlla la pillola, premi <b>Deploy
payload</b> e/o <b>START STREAMING</b></td></tr>
<tr><td>I colori sembrano sbagliati</td>
<td>Hai toccato <code>gain</code> → rimettilo a 1.0 e Apply</td></tr>
<tr><td>Immagine decentrata / barra nera</td>
<td>Allineamento → regola <code>shift px</code> con numeri pari</td></tr>
<tr><td>Discord mostra 720p</td>
<td>Il motore di Discord limita ogni camera a 720p → usa OBS per il
1080p</td></tr>
<tr><td>OBS vede la camera, Discord no</td>
<td>Discord era aperto prima dell'installazione del driver → chiudilo
COMPLETAMENTE (anche dalla tray) e riaprilo</td></tr>
<tr><td>Tutto il resto</td>
<td>Leggi <code>pscapture-app.log</code> (accanto all'exe o in
<code>%TEMP%</code>) e il log della console
<code>/data/pscapture.log</code> via FTP</td></tr>
</table>
<section id="build" class="reveal">
<div class="sec-head">
<div class="kicker">Build dai sorgenti</div>
<h2>Due metà, due toolchain.</h2>
<p class="sub">La repo contiene solo i sorgenti; i binari pronti sono nelle <a href="https://github.com/fstat13/PSCapture/releases">Releases</a>. La guida completa (installazione toolchain, verifiche, trappole) è in <a href="https://github.com/fstat13/PSCapture/blob/main/docs/BUILD.it.md">BUILD.it.md</a>.</p>
</div>
<h2 id="build">Build dai sorgenti</h2>
<div class="build-step">
<div class="num">Passo 1 · console</div>
<h3>Payload → <code>pscapture.elf</code></h3>
<p>Servono Docker e l'immagine del PS5 payload SDK (prospero-clang; va bene qualsiasi tag, qui <code>ps5sdk:18</code>).</p>
<div class="codeblock"><pre><span class="p">$ </span>docker run --rm -v "$PWD/payload:/payload" -w /payload ps5sdk:18 make</pre></div>
</div>
<p style="color:var(--dim)">Solo sorgenti — nella repo non ci sono binari.
Due metà, due toolchain. Il walk-through completo (installazione toolchain,
verifiche, trappole) è in <a href="BUILD.it.md">docs/BUILD.it.md</a>.</p>
<h3>1 · Payload console → <code>pscapture.elf</code></h3>
<pre><span class="c"># servono Docker + l'immagine del PS5 payload SDK (prospero-clang; qui tag ps5sdk:18)</span>
docker run --rm -v "&lt;repo&gt;/payload:/payload" -w /payload ps5sdk:18 make</pre>
<h3>2 · Lato PC → <code>pscapture.exe</code> + <code>pscapture.dll</code></h3>
<p style="color:var(--dim)">Serve MinGW-w64 (<code>gcc</code> +
<code>windres</code>). <b>L'ordine è obbligatorio</b>: il
<code>.res</code> incastra driver e payload così come sono su disco.</p>
<pre><span class="c"># dalla radice della repo</span>
cp payload/pscapture.elf pc/ <span class="c"># viene incastrato così com'è</span>
<div class="build-step">
<div class="num">Passo 2 · PC</div>
<h3>App e driver → <code>pscapture.exe</code> + <code>pscapture.dll</code></h3>
<p>Serve MinGW-w64 (<code>gcc</code> + <code>windres</code>). <b>L'ordine conta</b>: il <code>.res</code> incorpora driver e payload esattamente come sono su disco.</p>
<div class="codeblock"><pre><span class="c"># dalla radice della repo</span>
cp payload/pscapture.elf pc/
cd pc
<span class="c"># 1) il driver della capture card (filtro sorgente DirectShow)</span>
gcc -O2 -shared -static -static-libgcc -o pscapture.dll ps5cam.c \
-lole32 -loleaut32 -luser32 -ladvapi32 -lws2_32
<span class="c"># 2) bundle: icona + payload + driver nell'oggetto risorse</span>
<span class="c"># 2) icona + payload + driver nell'oggetto risorse</span>
windres pscapture.rc -O coff -o pscapture.res
<span class="c"># 3) l'app a file unico</span>
<span class="c"># 3) l'app in un unico file</span>
gcc -O2 -mwindows -o pscapture.exe pscapture.c pscapture.res \
-lws2_32 -lshell32 -ladvapi32 -luxtheme</pre>
-lws2_32 -lshell32 -ladvapi32 -luxtheme</pre></div>
</div>
<details>
<summary>Perché quest'ordine? (e i due errori classici di build)</summary>
<p><b>windres incastra i file come sono su disco:</b> se la DLL o il
payload vengono ricompilati dopo il passo 2, l'exe continua a portarsi
dentro quelli vecchi. Rebuild = DLL → windres → exe, ogni volta.<br>
<b><code>-static -static-libgcc</code> sulla DLL è obbligatorio:</b>
senza, Windows può rifiutarsi di caricare il driver con il cryptico
“impossibile trovare il modulo specificato” (runtime mingw mancante).</p>
</details>
<div class="callout info" style="margin-top:22px">
<svg class="i"><use href="#i-info"/></svg>
<p><b>I due errori classici.</b> Se ricompili la DLL o il payload <i>dopo</i> <code>windres</code>, l'exe si porta dentro quelli vecchi: l'ordine è sempre DLL → windres → exe. E <code>-static -static-libgcc</code> sulla DLL è obbligatorio: senza, Windows può rifiutarsi di caricare il driver con “impossibile trovare il modulo specificato” (manca il runtime MinGW).</p>
</div>
</section>
<h2>Requisiti</h2>
<table>
<tr><th>Dove</th><th>Cosa</th></tr>
<tr><td>PS5</td><td>Jailbroken (payload loader + FTP + autoloader pldmgr)</td></tr>
<tr><td>PS5</td><td>Firmware testato: <b>13.60</b> — vedi la FAQ per le altre versioni</td></tr>
<tr><td>PC</td><td>Windows 10/11 64-bit, rete cablata consigliata</td></tr>
<tr><td>Rete</td><td>Entrambi sulla stessa LAN (il gigabit basta per 1080p30)</td></tr>
</table>
<section id="faq" class="reveal">
<div class="sec-head">
<div class="kicker">FAQ</div>
<h2>Domande.</h2>
</div>
<div class="faq">
<details><summary>Funziona su firmware diversi dal 13.60?</summary>
<div class="body"><p>Il lato PC funziona con qualsiasi firmware: app e driver usano solo TCP, FTP e memoria condivisa. La parte legata alla versione è il payload della console, sviluppato e tarato sul <b>13.60</b> usando gli ancoraggi del display engine di quel firmware. Su altre versioni può servire un piccolo porting per ritrovare gli stessi ancoraggi. <a href="https://github.com/fstat13/PSCapture/issues">Apri una issue</a> indicando il tuo firmware.</p></div></details>
<details><summary>Perché in chiamata su Discord la webcam è a 720p?</summary>
<div class="body"><p>È il motore vocale di Discord: chiede 1280×720 a ogni webcam. In OBS, PS5 Capture va a 1920×1080 pieni. La condivisione schermo di Discord arriva a 1080p60 con Nitro.</p></div></details>
<details><summary>Servono una capture card o il Remote Play?</summary>
<div class="body"><p>No. PSCapture non usa mai il Remote Play: il payload legge il display engine direttamente sulla console e manda i frame grezzi sulla tua rete locale.</p></div></details>
<details><summary>Cattura anche l'audio del gioco?</summary>
<div class="body"><p>Non ancora. Per ora PSCapture trasmette solo il video.</p></div></details>
<details><summary>Funziona su macOS o Linux?</summary>
<div class="body"><p>Il lato PC è solo per Windows, perché la webcam è un dispositivo DirectShow.</p></div></details>
<details><summary>Come si disinstalla?</summary>
<div class="body"><ol>
<li>Chiudi l'app.</li>
<li>Esegui <code>rundll32 "%ProgramFiles%\PSCapture\pscapture.dll",DllUnregisterServer</code> per rimuovere la webcam.</li>
<li>Cancella <code>C:\Program Files\PSCapture</code>.</li>
<li>Facoltativo, sulla console: cancella <code>/data/pldmgr/payloads/pscapture/</code> così l'autoloader smette di avviarlo.</li>
</ol></div></details>
</div>
</section>
<h2 id="faq">FAQ</h2>
<div class="band reveal">
<div>
<h2>Ti è utile? Lascia una stella.</h2>
<p>PSCapture è gratuito. Una stella su GitHub aiuta altri appassionati di homebrew PS5 a trovarlo.</p>
</div>
<div class="cta">
<a class="btn btn-primary" href="https://github.com/fstat13/PSCapture"><svg class="i"><use href="#i-star"/></svg>Metti una stella <span class="stars" data-stars hidden></span></a>
<a class="btn btn-ghost" href="https://github.com/fstat13/PSCapture/issues">Segnala un problema</a>
</div>
</div>
</main>
<details>
<summary>Funziona su firmware precedenti al 13.60?</summary>
<p>Il lato PC funziona su qualsiasi firmware: app e driver parlano solo
TCP/FTP e memoria condivisa. La parte version-specifica è il payload
della console — è stato compilato e tarato sul <b>13.60</b>, usando gli
ancoraggi del display engine trovati su quel firmware. Su altri
firmware può servire un piccolo lavoro di porting (cioè ritrovare gli
stessi ancoraggi su quel sistema). Se hai un'altra versione, apri una
issue con il tuo firmware e possiamo aggiungere un profilo.</p>
</details>
<details>
<summary>Perché la camera in chiamata è 720p e non 1080p?</summary>
<p>Quel limite è del motore vocale di Discord, non di PSCapture: chiede
1280x720 a ogni camera. Per la qualità piena usa OBS (dove PS5 Capture
va a 1920x1080) o la condivisione schermo di Discord, che arriva a
1080p60 con Nitro.</p>
</details>
<details>
<summary>Servono una capture card o il Remote Play?</summary>
<p>No. PSCapture non tocca mai il Remote Play: il payload legge il display
engine sulla console stessa e manda i frame grezzi sulla tua LAN.</p>
</details>
<details>
<summary>Come si disinstalla?</summary>
<p>Chiudi l'app, poi <code>rundll32 "%ProgramFiles%\PSCapture\pscapture.dll",DllUnregisterServer</code>,
cancella <code>C:\Program Files\PSCapture</code>, e opzionalmente
cancella <code>/data/pldmgr/payloads/pscapture/</code> sulla console
così l'autoloader smette di avviare il payload.</p>
</details>
<footer>
PSCapture — porta lo schermo della PS5 ovunque nella tua LAN, senza card e senza cloud.
<br><b>di fstat13</b> &middot; source-available. Fatto per la community homebrew. <span class="ok">Gioca responsabilmente.</span>
<br><br>
Documenti: <a href="GUIDE.it.md">guida a prova di scemo (md)</a> &middot;
<a href="BUILD.it.md">build dai sorgenti (md)</a> &middot;
<a href="INSTALLER.md">cosa fa l'installer dentro</a> &middot;
<a href="REVERSE_ENGINEERING.md">reverse engineering</a> &middot;
<a href="ENGINEERING_LOG.md">engineering log</a>
</footer>
</div>
<footer>
<div class="wrap">
<div class="foot">
<div>
<a class="logo" href="#top"><img src="pscapture.png" alt="">PSCapture</a>
<p>Lo schermo della PS5 ovunque nella tua rete. Senza card, senza cloud.<br>di <b>fstat13</b></p>
</div>
<div>
<h4>Guide</h4>
<ul>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/GUIDE.it.md">Guida passo-passo</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/BUILD.it.md">Build dai sorgenti</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/INSTALLER.md">Cosa fa l'installer (EN)</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/REVERSE_ENGINEERING.md">Reverse engineering (EN)</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/docs/ENGINEERING_LOG.md">Engineering log (EN)</a></li>
</ul>
</div>
<div>
<h4>Progetto</h4>
<ul>
<li><a href="https://github.com/fstat13/PSCapture">GitHub</a></li>
<li><a href="https://github.com/fstat13/PSCapture/releases">Releases</a></li>
<li><a href="https://github.com/fstat13/PSCapture/issues">Issues</a></li>
<li><a href="https://github.com/fstat13/PSCapture/blob/main/LICENSE">Licenza</a></li>
</ul>
</div>
</div>
<div class="legal">
<span>© 2026 fstat13 · source-available</span>
<span>Non affiliato a Sony Interactive Entertainment. “PlayStation” e “PS5” sono marchi dei rispettivi proprietari.</span>
</div>
</div>
</footer>
<script src="main.js"></script>
</body>
</html>
+107
View File
@@ -0,0 +1,107 @@
/* PSCapture — page behaviour shared by index.html / index.it.html */
(function () {
var doc = document.documentElement;
doc.classList.remove('no-js');
var it = doc.lang === 'it';
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/* nav border once the page scrolls */
var nav = document.querySelector('.nav');
function onScroll() { nav.classList.toggle('scrolled', window.scrollY > 8); }
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
/* app mock: replay the real start-up log, then flip the pill to STREAMING */
var log = document.querySelector('.app-log');
var pill = document.querySelector('.pill');
if (log && pill) {
var lines = Array.prototype.slice.call(log.children);
var live = function () {
pill.classList.add('live');
pill.lastChild.textContent = ' STREAMING 29.8 fps';
};
/* the HTML ships in the final state, so no-JS and reduced-motion visitors see it as-is */
if (!reduce) {
pill.classList.remove('live');
pill.lastChild.textContent = ' AVVIO...';
lines.forEach(function (l) { l.style.display = 'none'; });
var i = 0;
(function next() {
if (i < lines.length) {
lines[i].style.display = '';
i++;
setTimeout(next, i === 1 ? 500 : 420 + Math.random() * 380);
} else {
live();
}
})();
}
}
/* copy buttons on code blocks (prompts and comments are not copied) */
document.querySelectorAll('.codeblock').forEach(function (block) {
var btn = document.createElement('button');
btn.className = 'copy';
btn.type = 'button';
btn.textContent = it ? 'Copia' : 'Copy';
btn.addEventListener('click', function () {
var pre = block.querySelector('pre').cloneNode(true);
pre.querySelectorAll('.p, .c').forEach(function (n) { n.remove(); });
var text = pre.textContent.split('\n')
.map(function (s) { return s.replace(/\s+$/, ''); })
.filter(function (s) { return s.length; })
.join('\n');
navigator.clipboard.writeText(text).then(function () {
btn.textContent = it ? 'Copiato ✓' : 'Copied ✓';
btn.classList.add('done');
setTimeout(function () {
btn.textContent = it ? 'Copia' : 'Copy';
btn.classList.remove('done');
}, 1600);
});
});
block.appendChild(btn);
});
/* troubleshooting filter */
var q = document.getElementById('q');
if (q) {
var issues = document.querySelectorAll('.issue');
var none = document.querySelector('.noresult');
q.addEventListener('input', function () {
var words = q.value.toLowerCase().trim().split(/\s+/).filter(Boolean);
var shown = 0;
issues.forEach(function (el) {
var txt = el.textContent.toLowerCase();
var ok = words.every(function (w) { return txt.indexOf(w) !== -1; });
el.hidden = !ok;
if (ok) shown++;
});
none.hidden = shown !== 0;
});
}
/* live star count on the GitHub buttons */
fetch('https://api.github.com/repos/fstat13/PSCapture')
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) {
if (!d || typeof d.stargazers_count !== 'number') return;
document.querySelectorAll('[data-stars]').forEach(function (el) {
el.textContent = d.stargazers_count.toLocaleString(doc.lang);
el.hidden = false;
});
})
.catch(function () {});
/* fade sections in as they scroll into view */
if ('IntersectionObserver' in window && !reduce) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); }
});
}, { rootMargin: '0px 0px -8% 0px' });
document.querySelectorAll('.reveal').forEach(function (el) { io.observe(el); });
} else {
document.querySelectorAll('.reveal').forEach(function (el) { el.classList.add('in'); });
}
})();
+395
View File
@@ -0,0 +1,395 @@
/* PSCapture — shared stylesheet for index.html / index.it.html */
:root{
color-scheme: dark;
--bg:#07090d;
--bg-2:#0b0e14;
--surface:#10141c;
--surface-2:#151b25;
--surface-3:#1b2230;
--line:#1f2733;
--line-2:#2a3445;
--fg:#eef2f8;
--fg-2:#c3cbd8;
--dim:#8a95a8;
--dim-2:#5f6b7f;
--accent:#2986eb; /* same blue as the app's START button */
--accent-2:#5aa8ff;
--accent-glow:rgba(41,134,235,.35);
--violet:#7c5cff;
--green:#22c55e;
--amber:#f59e0b;
--red:#ef4444;
--radius:16px;
--radius-sm:10px;
--maxw:1120px;
--font:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
--mono:"JetBrains Mono",ui-monospace,Consolas,"Cascadia Mono",monospace;
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:84px;-webkit-text-size-adjust:100%}
body{
margin:0;background:var(--bg);color:var(--fg);
font:16px/1.65 var(--font);
-webkit-font-smoothing:antialiased;
overflow-x:hidden;
}
img{max-width:100%;display:block}
a{color:var(--accent-2);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px}
b,strong{color:var(--fg);font-weight:600}
::selection{background:var(--accent);color:#fff}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px}
@media (max-width:600px){.wrap{padding:0 16px}}
/* ───────── nav ───────── */
.nav{
position:sticky;top:0;z-index:50;
background:rgba(7,9,13,.72);
backdrop-filter:saturate(160%) blur(14px);
-webkit-backdrop-filter:saturate(160%) blur(14px);
border-bottom:1px solid transparent;
transition:border-color .2s;
}
.nav.scrolled{border-bottom-color:var(--line)}
.nav .wrap{display:flex;align-items:center;gap:20px;height:64px}
.logo{display:flex;align-items:center;gap:10px;color:var(--fg);font-weight:700;letter-spacing:-.01em}
.logo:hover{text-decoration:none}
.logo img{width:30px;height:30px;border-radius:8px}
.nav-links{display:flex;gap:4px;margin-left:auto}
.nav-links a{color:var(--dim);font-size:14px;padding:6px 10px;border-radius:8px}
.nav-links a:hover{color:var(--fg);background:var(--surface);text-decoration:none}
.nav-right{display:flex;align-items:center;gap:8px}
.lang{font-size:13px;color:var(--dim);border:1px solid var(--line-2);border-radius:999px;padding:4px 11px}
.lang:hover{color:var(--fg);text-decoration:none;border-color:var(--dim-2)}
@media (max-width:860px){.nav-links{display:none}.nav-right{margin-left:auto}}
/* ───────── buttons ───────── */
.btn{
display:inline-flex;align-items:center;gap:9px;
font:600 15px/1 var(--font);
padding:13px 20px;border-radius:12px;border:1px solid transparent;
cursor:pointer;transition:transform .15s,background .15s,border-color .15s,box-shadow .15s;
white-space:nowrap;
}
.btn:hover{text-decoration:none;transform:translateY(-1px)}
.btn svg{width:18px;height:18px;flex:none}
.btn-primary{background:var(--accent);color:#fff;box-shadow:0 8px 30px -8px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.18)}
.btn-primary:hover{background:#3592f5}
.btn-ghost{background:var(--surface);color:var(--fg);border-color:var(--line-2)}
.btn-ghost:hover{border-color:var(--dim-2)}
.btn-sm{padding:8px 13px;font-size:13.5px;border-radius:9px}
/* ───────── hero ───────── */
.hero{position:relative;padding:72px 0 56px;isolation:isolate}
.hero::before{
content:"";position:absolute;inset:-64px 0 0;z-index:-1;
background:
radial-gradient(600px 380px at 12% 8%, rgba(41,134,235,.22), transparent 70%),
radial-gradient(520px 360px at 88% 18%, rgba(124,92,255,.16), transparent 70%);
}
.hero::after{
content:"";position:absolute;inset:-64px 0 0;z-index:-1;
background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px);
background-size:56px 56px;opacity:.28;
-webkit-mask-image:radial-gradient(ellipse 70% 60% at 50% 20%,#000 30%,transparent 75%);
mask-image:radial-gradient(ellipse 70% 60% at 50% 20%,#000 30%,transparent 75%);
}
.hero-grid{display:grid;grid-template-columns:1fr 1.08fr;gap:56px;align-items:center}
@media (max-width:960px){.hero-grid{grid-template-columns:1fr;gap:44px}}
.eyebrow{
display:inline-flex;align-items:center;gap:8px;
font-size:13px;color:var(--fg-2);
border:1px solid var(--line-2);background:rgba(16,20,28,.7);
padding:5px 12px 5px 8px;border-radius:999px;
}
.eyebrow .tagv{background:var(--accent);color:#fff;font-weight:700;font-size:11.5px;padding:2px 8px;border-radius:999px}
.hero h1{
font-size:clamp(38px,5.6vw,62px);line-height:1.04;letter-spacing:-.035em;
margin:20px 0 18px;font-weight:800;
}
.grad{background:linear-gradient(100deg,#fff 10%,var(--accent-2) 55%,var(--violet));-webkit-background-clip:text;background-clip:text;color:transparent}
.lead{font-size:18.5px;color:var(--fg-2);margin:0 0 30px;max-width:540px}
.cta{display:flex;gap:12px;flex-wrap:wrap}
.hero-meta{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:26px;color:var(--dim);font-size:14px}
.hero-meta span{display:inline-flex;align-items:center;gap:7px}
.hero-meta svg{width:16px;height:16px;color:var(--green)}
/* ───────── app mock ───────── */
.app{
position:relative;
background:#10141b;border:1px solid var(--line-2);border-radius:14px;
box-shadow:0 40px 80px -30px rgba(0,0,0,.8),0 0 0 1px rgba(255,255,255,.02),0 0 120px -40px var(--accent-glow);
overflow:hidden;font-size:13px;
}
.app-bar{display:flex;align-items:center;gap:10px;background:#0b0e13;border-bottom:1px solid #202733;padding:10px 14px}
.app-bar img{width:18px;height:18px;border-radius:4px}
.app-bar .t{color:var(--fg-2);font-size:12.5px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.app-bar .w{display:flex;gap:14px;color:var(--dim-2);font-size:12px}
.app-body{padding:18px 18px 16px}
.app-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
.app-title{font-weight:700;font-size:17px;letter-spacing:-.01em}
.app-title small{display:block;color:var(--dim);font-weight:400;font-size:12px}
.pill{
display:inline-flex;align-items:center;gap:8px;white-space:nowrap;
background:#1a2130;border:1px solid #273042;border-radius:999px;
padding:6px 12px;font:600 11.5px/1 var(--mono);letter-spacing:.02em;color:var(--amber);
transition:color .3s;
}
.pill i{width:8px;height:8px;border-radius:50%;background:currentColor;box-shadow:0 0 0 0 currentColor;animation:pulse 1.6s infinite}
.pill.live{color:var(--green)}
@keyframes pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb,currentColor 60%,transparent)}70%{box-shadow:0 0 0 7px transparent}100%{box-shadow:0 0 0 0 transparent}}
.app-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:10px}
.app-lbl{color:var(--dim);font-size:12px}
.app-in{background:#0c0f15;border:1px solid #202733;border-radius:7px;padding:6px 10px;color:#cbd3e0;font:12px var(--mono)}
.app-btn{background:#1a2029;border:1px solid #2c3646;border-radius:7px;padding:6px 11px;color:var(--fg-2);font-size:12px}
.app-btn.big{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:700;padding:12px 22px;font-size:13px;letter-spacing:.02em}
.app-btn.stop{padding:12px 18px;font-weight:600}
.app-log{
margin-top:14px;background:#0c0f15;border:1px solid #202733;border-radius:9px;
padding:12px 14px;height:196px;overflow:hidden;
font:11.5px/1.75 var(--mono);color:#aeb8c8;
display:flex;flex-direction:column;justify-content:flex-end;
}
.app-log div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;animation:logIn .35s ease-out both}
.app-log .ok{color:#7ee2a5}
.app-log .hl{color:#8fc3ff}
@keyframes logIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (max-width:520px){
.app-bar .w{display:none}
.app-knobs{display:none}
}
/* ───────── sections ───────── */
section{padding:84px 0 0}
.sec-head{max-width:720px;margin-bottom:34px}
.kicker{font:600 12.5px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--accent-2);margin-bottom:14px}
h2{font-size:clamp(28px,3.6vw,40px);line-height:1.12;letter-spacing:-.028em;margin:0 0 14px;font-weight:750}
h3{font-size:18px;letter-spacing:-.01em;margin:0 0 8px;font-weight:650}
.sub{color:var(--dim);font-size:17px;margin:0}
p{margin:0 0 14px}
/* stats */
.stats{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-2);overflow:hidden;margin-top:12px}
.stat{padding:22px 24px;border-left:1px solid var(--line)}
.stat:first-child{border-left:0}
.stat .n{font-size:28px;font-weight:750;letter-spacing:-.03em}
.stat .l{color:var(--dim);font-size:13.5px}
@media (max-width:760px){.stats{grid-template-columns:repeat(2,1fr)}.stat:nth-child(3){border-left:0}.stat:nth-child(n+3){border-top:1px solid var(--line)}}
/* feature cards */
.grid{display:grid;gap:16px}
.g3{grid-template-columns:repeat(3,1fr)}
.g2{grid-template-columns:repeat(2,1fr)}
@media (max-width:900px){.g3{grid-template-columns:1fr 1fr}}
@media (max-width:640px){.g3,.g2{grid-template-columns:1fr}}
.card{
position:relative;background:linear-gradient(180deg,var(--surface),var(--bg-2));
border:1px solid var(--line);border-radius:var(--radius);padding:24px;
transition:border-color .2s,transform .2s;
}
.card:hover{border-color:var(--line-2)}
.card p{color:var(--dim);font-size:15px;margin:0}
.ico{
width:42px;height:42px;border-radius:11px;display:grid;place-items:center;margin-bottom:16px;
background:rgba(41,134,235,.12);color:var(--accent-2);border:1px solid rgba(41,134,235,.25);
}
.ico svg{width:21px;height:21px}
.ico.v{background:rgba(124,92,255,.12);color:#a996ff;border-color:rgba(124,92,255,.28)}
.ico.g{background:rgba(34,197,94,.1);color:#5fdc8f;border-color:rgba(34,197,94,.25)}
.ico.a{background:rgba(245,158,11,.1);color:#fbbf4a;border-color:rgba(245,158,11,.25)}
/* pipeline */
.pipe{display:grid;grid-template-columns:1fr auto 1fr auto 1fr auto 1fr;align-items:stretch;gap:0}
.node{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:20px}
.node .where{font:600 11px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--dim-2);margin-bottom:10px}
.node h3{font-size:16px}
.node p{color:var(--dim);font-size:14px;margin:0}
.node.end{background:linear-gradient(160deg,rgba(41,134,235,.14),var(--surface) 60%);border-color:rgba(41,134,235,.35)}
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.chip{font-size:12px;color:var(--fg-2);background:var(--surface-3);border:1px solid var(--line-2);border-radius:999px;padding:3px 10px}
.link{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0 10px;color:var(--dim-2);min-width:64px}
.link span{font:500 10.5px/1.2 var(--mono);text-align:center;margin-bottom:6px;color:var(--dim)}
.link .line{position:relative;width:100%;height:2px;background:linear-gradient(90deg,var(--line-2),var(--accent),var(--line-2));background-size:200% 100%;animation:flow 2.2s linear infinite;border-radius:2px}
.link .line::after{content:"";position:absolute;right:-1px;top:-4px;border:5px solid transparent;border-left-color:var(--accent);border-right:0}
@keyframes flow{from{background-position:100% 0}to{background-position:-100% 0}}
@media (max-width:960px){
.pipe{grid-template-columns:1fr}
.link{flex-direction:row;gap:10px;padding:10px 0;min-height:44px}
.link span{margin:0;order:2}
.link .line{width:2px;height:28px;background:linear-gradient(180deg,var(--line-2),var(--accent),var(--line-2));background-size:100% 200%}
.link .line::after{right:-4px;top:auto;bottom:-1px;border:5px solid transparent;border-top-color:var(--accent);border-bottom:0}
}
/* requirements */
.req{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:24px}
.req-h{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.req-h .ico{margin:0}
.req-h h3{margin:0}
.req ul{list-style:none;margin:0;padding:0}
.req li{display:flex;gap:12px;padding:11px 0;border-top:1px solid var(--line);font-size:15px;color:var(--fg-2)}
.req li:first-child{border-top:0;padding-top:0}
.req li svg{width:18px;height:18px;flex:none;margin-top:3px;color:var(--green)}
.req li.opt svg{color:var(--dim-2)}
.req li small{color:var(--dim);display:block;font-size:13.5px}
.tagopt{font:600 10.5px/1 var(--mono);color:var(--dim);border:1px solid var(--line-2);border-radius:5px;padding:2px 6px;margin-left:6px;vertical-align:2px;text-transform:uppercase;letter-spacing:.06em}
/* steps */
.steps{list-style:none;margin:0;padding:0;counter-reset:s;position:relative}
.steps>li{counter-increment:s;position:relative;padding:0 0 30px 68px}
.steps>li::before{
content:counter(s);position:absolute;left:0;top:-2px;
width:44px;height:44px;border-radius:13px;display:grid;place-items:center;
font:700 17px/1 var(--font);color:#fff;
background:linear-gradient(160deg,var(--accent),#1a5fb4);
box-shadow:0 8px 24px -8px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.2);
}
.steps>li::after{content:"";position:absolute;left:21px;top:50px;bottom:6px;width:2px;background:linear-gradient(var(--line-2),transparent)}
.steps>li:last-child::after{display:none}
.steps h3{margin:6px 0 6px;font-size:18.5px}
.steps p{color:var(--dim);margin:0}
/* callouts */
.callout{display:flex;gap:14px;border-radius:var(--radius);padding:18px 20px;border:1px solid;font-size:15px}
.callout svg{width:20px;height:20px;flex:none;margin-top:2px}
.callout p{margin:0;color:inherit}
.callout.info{background:rgba(41,134,235,.07);border-color:rgba(41,134,235,.3);color:#cfe3ff}
.callout.info svg{color:var(--accent-2)}
.callout.warn{background:rgba(245,158,11,.06);border-color:rgba(245,158,11,.3);color:#f6e2bd}
.callout.warn svg{color:var(--amber)}
/* terminal-style log legend */
.term{background:#0a0d12;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.term-bar{display:flex;align-items:center;gap:8px;padding:11px 16px;border-bottom:1px solid var(--line);background:var(--bg-2);font:12px var(--mono);color:var(--dim)}
.term-bar i{width:10px;height:10px;border-radius:50%;background:var(--line-2)}
.term-bar b{margin-left:8px;font-weight:500;color:var(--dim)}
.term-row{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:20px;padding:14px 18px;border-top:1px solid var(--line)}
.term-row:first-of-type{border-top:0}
.term-row code{background:none;border:0;padding:0;color:#9fd0ff;font-size:13px;display:block;word-break:break-word}
.term-row .m{color:var(--fg-2);font-size:14.5px}
.term-row.ok code{color:#7ee2a5}
@media (max-width:700px){.term-row{grid-template-columns:1fr;gap:6px}}
/* app targets */
.target{display:flex;flex-direction:column}
.target .res{margin-top:auto;padding-top:16px;display:flex;align-items:center;justify-content:space-between;gap:10px;border-top:1px dashed var(--line-2);margin-top:18px;font-size:13.5px;color:var(--dim)}
.target .res b{font:700 15px var(--mono);color:var(--fg)}
.target .path{font-size:14px;color:var(--fg-2);margin-top:6px}
/* knobs */
.knob .k{display:inline-block;font:600 14px var(--mono);color:var(--fg);background:var(--surface-3);border:1px solid var(--line-2);border-radius:7px;padding:3px 9px;margin-bottom:12px}
.knob ul{margin:12px 0 0;padding-left:18px;color:var(--dim);font-size:14.5px}
.knob li{margin:4px 0}
/* troubleshooting */
.search{position:relative;margin-bottom:16px}
.search svg{position:absolute;left:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--dim-2)}
.search input{
width:100%;font:15px var(--font);color:var(--fg);
background:var(--surface);border:1px solid var(--line-2);border-radius:12px;
padding:14px 16px 14px 46px;outline:none;transition:border-color .15s,box-shadow .15s;
}
.search input::placeholder{color:var(--dim-2)}
.search input:focus{border-color:var(--accent);box-shadow:0 0 0 4px rgba(41,134,235,.15)}
.issues{display:grid;gap:10px}
.issue{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);gap:24px;background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:18px 20px}
.issue .sym{font-weight:600;color:var(--fg)}
.issue .sym code{font-weight:400}
.issue .fix{color:var(--dim);font-size:15px}
.issue .fix b{color:var(--fg-2)}
.issue .cause{display:block;font-size:12px;font-family:var(--mono);color:var(--dim-2);text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}
.issue[hidden]{display:none}
.noresult{color:var(--dim);padding:18px;text-align:center;border:1px dashed var(--line-2);border-radius:14px}
@media (max-width:700px){.issue{grid-template-columns:1fr;gap:10px}}
/* code */
code,kbd{
font:.86em/1.5 var(--mono);color:#cfe3ff;
background:var(--surface-3);border:1px solid var(--line-2);border-radius:6px;padding:1px 6px;
overflow-wrap:anywhere;
}
.codeblock{position:relative;margin:12px 0 0}
.codeblock pre{
margin:0;background:#0a0d12;border:1px solid var(--line);border-radius:14px;
padding:18px 20px;overflow-x:auto;font:13.5px/1.7 var(--mono);color:#d6e4f5;
}
.codeblock pre .c{color:#5d6b80}
.codeblock pre .p{color:var(--accent-2);user-select:none}
.copy{
position:absolute;top:10px;right:10px;
font:500 12px var(--font);color:var(--dim);
background:var(--surface-2);border:1px solid var(--line-2);border-radius:8px;
padding:5px 10px;cursor:pointer;opacity:.85;transition:all .15s;
}
.copy:hover{color:var(--fg);opacity:1}
.copy.done{color:var(--green);border-color:rgba(34,197,94,.4)}
.build-step{margin-top:34px}
.build-step:first-child{margin-top:0}
.build-step .num{font:600 12px var(--mono);color:var(--accent-2);letter-spacing:.08em;text-transform:uppercase}
.build-step h3{margin:6px 0 6px;font-size:20px}
.build-step>p{color:var(--dim);margin:0}
/* faq */
.faq{display:grid;gap:10px}
details{background:var(--surface);border:1px solid var(--line);border-radius:14px;transition:border-color .2s}
details[open]{border-color:var(--line-2)}
summary{
list-style:none;cursor:pointer;padding:18px 54px 18px 20px;position:relative;
font-weight:600;font-size:16px;
}
summary::-webkit-details-marker{display:none}
summary::after{
content:"";position:absolute;right:22px;top:50%;width:9px;height:9px;
border-right:2px solid var(--dim);border-bottom:2px solid var(--dim);
transform:translateY(-70%) rotate(45deg);transition:transform .2s;
}
details[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
details .body{padding:0 20px 18px;color:var(--dim);font-size:15px}
details .body p:last-child{margin:0}
details .body ol{margin:0;padding-left:20px}
details .body li{margin:6px 0}
/* cta band */
.band{
margin-top:96px;position:relative;overflow:hidden;
border:1px solid rgba(41,134,235,.3);border-radius:22px;padding:48px 40px;
background:
radial-gradient(500px 260px at 0% 0%,rgba(41,134,235,.25),transparent 70%),
radial-gradient(500px 260px at 100% 100%,rgba(124,92,255,.18),transparent 70%),
var(--bg-2);
display:flex;align-items:center;justify-content:space-between;gap:28px;flex-wrap:wrap;
}
.band h2{margin:0 0 6px;font-size:clamp(24px,3vw,32px)}
.band p{margin:0;color:var(--fg-2)}
@media (max-width:600px){.band{padding:32px 22px}}
/* footer */
footer{margin-top:80px;border-top:1px solid var(--line);padding:44px 0 56px;color:var(--dim);font-size:14px}
.foot{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:32px}
@media (max-width:760px){.foot{grid-template-columns:1fr}}
.foot h4{margin:0 0 12px;font-size:12px;font-family:var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--dim-2);font-weight:600}
.foot ul{list-style:none;margin:0;padding:0}
.foot li{margin:7px 0}
.foot a{color:var(--fg-2)}
.foot .logo{margin-bottom:12px}
.legal{margin-top:36px;padding-top:22px;border-top:1px solid var(--line);font-size:12.5px;color:var(--dim-2);display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap}
/* reveal on scroll */
.reveal{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s ease}
.reveal.in{opacity:1;transform:none}
.no-js .reveal{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
*,*::before,*::after{animation:none!important;transition:none!important}
.reveal{opacity:1;transform:none}
}
/* star counter inside GitHub buttons */
.stars{font:600 12.5px/1 var(--mono);color:var(--fg-2);background:var(--surface-3);border:1px solid var(--line-2);border-radius:6px;padding:3px 7px;margin-left:2px}
.btn-primary .stars{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.22);color:#fff}
/* inline svg icons (sprite in each page) */
.i{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}