Lecture : repli CPU quand le GPU sature, plus de spinner infini

Serveur chargé ou VRAM limite : NVENC refuse d'ouvrir l'encodeur
(OpenEncodeSessionEx failed: out of memory), FFmpeg meurt en une
seconde et la playlist part en 502. Chaque relance retombait sur le
même GPU saturé : le flux restait illisible.

- Serveur : un échec imputable au GPU (NVENC, CUDA, mémoire) relance
  la session une fois en libx264, sous le même identifiant — les
  segments suivent sans que le lecteur s'en aperçoive. Direct, VOD et
  enregistrements
- Le GPU est écarté 2 min après une panne : les sessions suivantes
  partent directement sur le CPU au lieu d'échouer une à une
- Source injoignable ou délai dépassé : pas de repli, il doublerait la
  charge d'un serveur déjà occupé
- Process.start qui échoue (serveur à court de ressources) : 502/503
  au lieu d'une exception remontée en 500
- Client : une erreur réseau fatale avant la première playlist
  appelait hls.startLoad(), qui ne relance pas ce chargement → spinner
  infini, aucune erreur, relances de page jamais déclenchées. L'erreur
  remonte désormais à la page
- mpegts.js : au-delà de 3 erreurs en 60 s, bascule sur HLS au lieu de
  recréer sans fin (un FFmpeg neuf toutes les 1,2 s)
- player_lite.html : 3 relances avant l'erreur, comme les autres pages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-08 21:28:26 +02:00
1 parent 6d40c38d30
commit db13c85c1d
5 files changed
+340 -74

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
/// Repli GPU → CPU des transcodages.
///
/// Quand la VRAM est saturée (plusieurs sessions NVENC, un autre processus
/// sur la carte) ou que le GPU dépasse son quota de sessions d'encodage,
/// FFmpeg échoue à l'ouverture de l'encodeur et meurt en une seconde. Sans
/// repli, le lecteur recevait un 502, relançait… et retombait sur le même
/// GPU saturé : le flux restait illisible tant que la carte était pleine.
/// Signatures FFmpeg d'un échec imputable au GPU NVIDIA (NVENC/NVDEC/CUDA).
///
/// N'est consulté que pour une session lancée sur le GPU : « opening
/// encoder » y désigne donc forcément h264_nvenc.
final _gpuFailurePattern = RegExp(
r'nvenc|cuda|cuvid|nvdec|OpenEncodeSession|No capable devices|'
r'out of memory|Device creation failed|hwaccel|opening encoder',
caseSensitive: false,
);
/// Vrai si [error] (stderr récent de FFmpeg) trahit une panne GPU, et non
/// une source injoignable ou un délai dépassé — qu'un repli CPU ne
/// réglerait pas, et qui doublerait inutilement la charge d'un serveur déjà
/// occupé.
bool isGpuFailure(String? error) =>
error != null && _gpuFailurePattern.hasMatch(error);
/// Mémorise une panne GPU récente pour que les sessions suivantes partent
/// directement sur le CPU, au lieu de payer chacune un échec NVENC avant
/// leur repli.
class GpuHealth {
final Duration cooldown;
final DateTime Function() _now;
DateTime? _unavailableUntil;
GpuHealth({
this.cooldown = const Duration(minutes: 2),
DateTime Function()? now,
}) : _now = now ?? DateTime.now;
bool get available =>
_unavailableUntil == null || !_now().isBefore(_unavailableUntil!);
void markFailed() => _unavailableUntil = _now().add(cooldown);
}