fix : opening link 2
This commit is contained in:
32
README.md
32
README.md
@@ -246,9 +246,37 @@ Le mode de sortie du profil est forcé sur « Simple » : c'est la section que c
|
|||||||
pilotent. Un paramètre refusé par la version d'OBS installée n'interrompt pas les autres —
|
pilotent. Un paramètre refusé par la version d'OBS installée n'interrompt pas les autres —
|
||||||
il apparaît dans le compte rendu affiché sous le bouton « Appliquer maintenant ».
|
il apparaît dans le compte rendu affiché sous le bouton « Appliquer maintenant ».
|
||||||
|
|
||||||
|
### Diagnostiquer une chute de performance
|
||||||
|
|
||||||
|
La fiche de l'agent sépare les deux goulots qu'OBS mesure distinctement. Les confondre mène
|
||||||
|
à régler le mauvais paramètre.
|
||||||
|
|
||||||
|
| Métrique | Ce qu'elle dit | Leviers |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Perdu · rendu** | La scène n'a pas été composée à temps | Méthode de capture, définition de la scène, accélération 3D du bureau |
|
||||||
|
| **Perdu · encodage** | La scène était prête, l'encodeur n'a pas suivi | Preset plus léger, encodeur matériel |
|
||||||
|
| **Rendu** (ms) | Durée moyenne de composition d'une image | Doit rester bien sous `1000 / fps` (33 ms à 30 fps) |
|
||||||
|
|
||||||
|
Les compteurs cumulent depuis le lancement d'OBS, d'où l'affichage en pourcentage : sous
|
||||||
|
1 %, il n'y a rien à faire ; au-delà de quelques pour cent, le poste indiqué est saturé.
|
||||||
|
|
||||||
|
Changer de preset ou d'encodeur ne corrige **que** la colonne encodage. Si c'est le rendu
|
||||||
|
qui décroche, le problème est en amont d'OBS.
|
||||||
|
|
||||||
|
### Xorg, Wayland et le coût de la capture
|
||||||
|
|
||||||
|
Sur une VM sans accélération 3D, la méthode de capture pèse lourd :
|
||||||
|
|
||||||
|
- **Wayland** : OBS passe par PipeWire, le compositeur lui remet les images déjà composées.
|
||||||
|
- **Xorg** : la capture d'écran XSHM recopie tout le tampon à chaque image. La capture de
|
||||||
|
fenêtre XComposite est nettement plus économe — à préférer systématiquement.
|
||||||
|
|
||||||
|
Xorg n'est nécessaire que pour le rappel du plein écran par xdotool. Si le cadrage se fait
|
||||||
|
côté OBS, Wayland est le meilleur choix en performance.
|
||||||
|
|
||||||
Un preset trop lourd pour la VM fait chuter les images par seconde : la qualité perçue
|
Un preset trop lourd pour la VM fait chuter les images par seconde : la qualité perçue
|
||||||
baisse alors malgré un meilleur CRF. Après un changement, surveille « FPS » et « Frames
|
baisse alors malgré un meilleur CRF. Après un changement, surveille « FPS » et les deux
|
||||||
perdues » sur la fiche de l'agent.
|
colonnes de frames perdues sur la fiche de l'agent.
|
||||||
|
|
||||||
## Historique par VM
|
## Historique par VM
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ type ObsSnapshot = Pick<
|
|||||||
| 'cpuUsage'
|
| 'cpuUsage'
|
||||||
| 'fps'
|
| 'fps'
|
||||||
| 'droppedFrames'
|
| 'droppedFrames'
|
||||||
|
| 'outputTotalFrames'
|
||||||
| 'renderSkippedFrames'
|
| 'renderSkippedFrames'
|
||||||
|
| 'renderTotalFrames'
|
||||||
|
| 'frameRenderTimeMs'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const RECONNECT_DELAY_MS = 5000;
|
const RECONNECT_DELAY_MS = 5000;
|
||||||
@@ -512,7 +515,10 @@ export class ObsController extends EventEmitter {
|
|||||||
cpuUsage: stats.cpuUsage,
|
cpuUsage: stats.cpuUsage,
|
||||||
fps: stats.activeFps,
|
fps: stats.activeFps,
|
||||||
droppedFrames: stats.outputSkippedFrames,
|
droppedFrames: stats.outputSkippedFrames,
|
||||||
|
outputTotalFrames: stats.outputTotalFrames,
|
||||||
renderSkippedFrames: stats.renderSkippedFrames,
|
renderSkippedFrames: stats.renderSkippedFrames,
|
||||||
|
renderTotalFrames: stats.renderTotalFrames,
|
||||||
|
frameRenderTimeMs: stats.averageFrameRenderTime,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.lastError = err instanceof Error ? err.message : String(err);
|
this.lastError = err instanceof Error ? err.message : String(err);
|
||||||
|
|||||||
@@ -532,8 +532,23 @@ export interface AgentStatus {
|
|||||||
/** Statistiques OBS. */
|
/** Statistiques OBS. */
|
||||||
cpuUsage?: number;
|
cpuUsage?: number;
|
||||||
fps?: number;
|
fps?: number;
|
||||||
|
/**
|
||||||
|
* Deux goulots distincts, à ne pas confondre — c'est eux qui désignent la
|
||||||
|
* cause d'une chute de performance.
|
||||||
|
*
|
||||||
|
* `renderSkipped` : la composition de la scène n'a pas tenu la cadence. En
|
||||||
|
* cause : la méthode de capture, la définition, ou un bureau sans
|
||||||
|
* accélération 3D. Changer d'encodeur n'y ferait rien.
|
||||||
|
*
|
||||||
|
* `droppedFrames` (encodage) : la scène était prête mais l'encodeur n'a pas
|
||||||
|
* suivi. Là, le preset et l'encodeur sont les bons leviers.
|
||||||
|
*/
|
||||||
droppedFrames?: number;
|
droppedFrames?: number;
|
||||||
|
outputTotalFrames?: number;
|
||||||
renderSkippedFrames?: number;
|
renderSkippedFrames?: number;
|
||||||
|
renderTotalFrames?: number;
|
||||||
|
/** Durée moyenne de composition d'une image, en millisecondes. */
|
||||||
|
frameRenderTimeMs?: number;
|
||||||
|
|
||||||
/** Statistiques machine (collectées par l'agent, pas par OBS). */
|
/** Statistiques machine (collectées par l'agent, pas par OBS). */
|
||||||
systemCpu?: number;
|
systemCpu?: number;
|
||||||
|
|||||||
@@ -102,7 +102,24 @@ export function AgentCard({
|
|||||||
<Metric label="Disque libre" value={formatBytes(status.diskFreeBytes)} />
|
<Metric label="Disque libre" value={formatBytes(status.diskFreeBytes)} />
|
||||||
<Metric label="CPU OBS" value={formatPercent(status.cpuUsage)} />
|
<Metric label="CPU OBS" value={formatPercent(status.cpuUsage)} />
|
||||||
<Metric label="FPS" value={status.fps ? status.fps.toFixed(0) : '—'} />
|
<Metric label="FPS" value={status.fps ? status.fps.toFixed(0) : '—'} />
|
||||||
<Metric label="Frames perdues" value={String(status.droppedFrames ?? '—')} />
|
<Metric
|
||||||
|
label="Rendu"
|
||||||
|
value={
|
||||||
|
status.frameRenderTimeMs === undefined
|
||||||
|
? '—'
|
||||||
|
: `${status.frameRenderTimeMs.toFixed(1)} ms`
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Metric
|
||||||
|
label="Perdu · rendu"
|
||||||
|
value={formatSkipped(status.renderSkippedFrames, status.renderTotalFrames)}
|
||||||
|
title="Composition de la scène trop lente : capture, définition, accélération 3D"
|
||||||
|
/>
|
||||||
|
<Metric
|
||||||
|
label="Perdu · encodage"
|
||||||
|
value={formatSkipped(status.droppedFrames, status.outputTotalFrames)}
|
||||||
|
title="Encodeur en retard : preset trop lourd ou CPU saturé"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@@ -260,15 +277,39 @@ function WatchStrip({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Metric({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
|
function Metric({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
mono,
|
||||||
|
title,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
mono?: boolean;
|
||||||
|
title?: string;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="metric">
|
<div className="metric" title={title}>
|
||||||
<span className="metric-label">{label}</span>
|
<span className="metric-label">{label}</span>
|
||||||
<span className={`metric-value${mono ? ' mono' : ''}`}>{value}</span>
|
<span className={`metric-value${mono ? ' mono' : ''}`}>{value}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Images perdues, en valeur absolue et en part du total.
|
||||||
|
*
|
||||||
|
* Les compteurs d'OBS cumulent depuis son lancement : « 4 200 » ne dit rien
|
||||||
|
* seul, « 4 200 (0,3 %) » dit que tout va bien, « 4 200 (38 %) » qu'il faut
|
||||||
|
* agir.
|
||||||
|
*/
|
||||||
|
function formatSkipped(skipped?: number, total?: number): string {
|
||||||
|
if (skipped === undefined) return '—';
|
||||||
|
if (!total) return String(skipped);
|
||||||
|
const share = (skipped / total) * 100;
|
||||||
|
return `${skipped} (${share < 0.1 && skipped > 0 ? '<0,1' : share.toFixed(1).replace('.', ',')} %)`;
|
||||||
|
}
|
||||||
|
|
||||||
function basename(filePath: string): string {
|
function basename(filePath: string): string {
|
||||||
const parts = filePath.split(/[\\/]/);
|
const parts = filePath.split(/[\\/]/);
|
||||||
return parts[parts.length - 1] ?? filePath;
|
return parts[parts.length - 1] ?? filePath;
|
||||||
|
|||||||
Reference in New Issue
Block a user