| 1 |
Perf + qualité |
W |
Aucun timeBeginPeriod, MMCSS ni priorité dans les helpers (grep vide). Toutes les attentes tombent sur le tick de 15,625 ms : main.cpp:1349 (sleep_until du writer), wasapi_loopback_capture.cpp:458 (poll 5 ms), audio_sample_utils.cpp:717, cursor-sampler.cpp:407. Mesuré : intervalles webcam multiples de 15,625 ms à ±0,4 ms près ; curseur à 46-48 ms pour 33 demandés. |
Webcam régulière, curseur 30 Hz, jitter WASAPI réduit (réduit le coussin de #916), cadence écran moins saccadée. |
S |
Faible. Un peu plus de conso. Windows 11 peut ignorer la demande d'un process bridé : ajouter SetProcessInformation(ProcessPowerThrottling) ou des timers CREATE_WAITABLE_TIMER_HIGH_RESOLUTION. |
Réutiliser analyze.js (section 3) : histogramme des intervalles webcam et curseur avant/après. Ligne [pacing]. |
| 2 |
Qualité |
W |
Le choix du format webcam classe la taille avant la cadence et exclut le MJPEG dès qu'un mode non compressé existe (webcam_format.cpp:42-48, :69-78). Preset par défaut 2160p (src/hooks/webcamCaptureTarget.ts:42-48). Hypothèse forte : une C920 (YUY2 2304×1536 à 2 i/s, 1920×1080 à 5 i/s) est capturée à 2 i/s. |
Webcam fluide sur les caméras USB 2, la majorité du parc. |
S à M |
Moyen : le MJPEG avec sortie RGB32 ne livrait aucun échantillon (commentaire de webcam_format_test.cpp:42-45). Tester MJPEG vers NV12. |
Ligne INFO: Native webcam format WxH@F du helper sur une C920 ou C270. delivered= / durée. |
| 3 |
Qualité (débit) |
W |
Profil Constrained Baseline, 1 référence, CAVLC (mesuré sur l'écran et la webcam). Aucun MF_MT_MPEG2_PROFILE (mf_encoder.cpp:708-718). Mesuré en réencodant via le même MFT NVENC : High gagne +1,7 dB PSNR (écran) et +1,9 VMAF (webcam) à 4 Mbit/s. |
15 à 25 % de débit à qualité égale, ou mieux de qualité au même débit. Utile car le zoom agrandit la source. |
S |
Faible. Tous les MFT matériels et le décodeur ffmpeg du compositeur gèrent High. Ne pas ajouter de B-frames (voir section 5). |
Section 3.4. Après patch : ffprobe doit lire High. |
| 4 |
Qualité |
L |
Conversion BT.601 sans balise : swscale sans sws_setColorspaceDetails (pipewire-capture/src/encoder.rs:758-789), aucune balise sur le codec (encoder.rs:347-373), scale_vaapi sans matrice (dmabuf_import.rs:269-273). Le compositeur décode en BT.709 limité (crates/compositor/src/vk_shaders/layer.wgsl:82). |
Supprime un décalage de teinte et de saturation à chaque export Linux. |
S |
Faible. Windows sert de modèle. |
Mire #FF0000/#00FF00/#0000FF, lecture YUV brute (section 4.3). |
| 5 |
Qualité |
W |
Piste écran sans balise couleur (mesuré : color_range/space/primaries = unknown). Le chemin CPU efface les balises (mf_encoder.cpp:758-762, :770-778) et laisse le MFT choisir la matrice RGB32 vers YUV. Hypothèse : matrice non garantie BT.709 (NVENC et le Video Processor MFT peuvent choisir BT.601). Webcam : range et matrice balisés, mais primaries et transfer lus « reserved » (mesuré, mf_encoder.cpp:740-743 ne pose ni MF_MT_VIDEO_PRIMARIES ni MF_MT_TRANSFER_FUNCTION). |
Couleurs identiques entre capture, aperçu et export. Fichiers lisibles correctement hors de l'app. |
S |
Faible. |
Section 4.3 : rouge pur attendu Y≈63, Cb≈102 en BT.709 ; Y≈81, Cb≈90 en BT.601. |
| 6 |
Synchro A/V |
W |
La vidéo écran est renumérotée en CFR par le sink writer : 1449 frames à 16,667 ms exactement (mesuré). Le commit c9ff08ac l'a prouvé : un blocage injecté de 300 ms ne laisse aucun trou. Or la boucle resynchronise sans rattraper (main.cpp:1341-1348). Chaque tick perdu décale donc toute la suite de la vidéo vers l'avant de l'audio, qui suit l'horloge. |
Synchro tenue sous charge (machines faibles, #510). |
S |
Faible. |
Test flash + bip sous charge CPU (section 4.2). Ligne [pacing] : frames / (elapsed × fps). |
| 7 |
Perf |
W |
Relecture GPU complète à chaque tick, même sans nouveau frame WGC (main.cpp:1258-1287). Chaque relecture fait CopyResource puis Map immédiat, donc attente GPU (mf_encoder.cpp:1091-1094), plus un nouveau buffer de 8 Mo par frame (:1643-1646) et une copie ligne à ligne (:1106-1109). Estimation 1080p60 : environ 475 Mo/s par étape. 4K60 : environ 2 Go/s. |
CPU et bande passante mémoire. Moins de ticks manqués (lie au rang 6). |
S (réutiliser le dernier échantillon) à M (anneau de 2 à 3 textures staging et pool de buffers) |
Faible. Une frame de latence avec l'anneau, sans effet sur les horodatages. |
typeperf "\Process(wgc-capture)\% Processor Time" et compteur GPU Engine, écran statique puis défilement, 60 s. |
| 8 |
Perf |
W |
Chemin GPU DXGI/NV12 désactivé par défaut (main.cpp:925-928) depuis #336 : aucun repli si l'échec survient en cours de prise. |
Supprime l'aller-retour GPU vers CPU vers GPU. Conversion BT.709 explicite (règle aussi le rang 5). |
L |
Moyen : c'est exactement ce qui a cassé #336. Il faut un repli à chaud vers le chemin CPU. |
Même mesure que le rang 7, avec OPENSCREEN_WGC_ENABLE_DXGI_INPUT=1. |
| 9 |
Qualité audio |
W, M, L, E |
Mix sans limiteur sur les trois helpers : écrêtage dur après le gain micro. Windows mixe en PCM16 après avoir quantifié chaque source (audio_sample_utils.cpp:459-467, :600). macOS : AudioTrackMixer.swift:609-613. Linux : capture.rs:137-142. Navigateur : src/lib/audioMix.ts:52-80. Mesuré : voix à -31,3 et -31,4 LUFS, crêtes à -12 et -15 dBFS. L'éditeur remonte ensuite jusqu'à +12 dB après l'AAC (PR #916). |
Pas d'écrêtage quand voix et son système se cumulent. Meilleur niveau avant AAC. Hypothèse : un signal très bas perd des détails sous le seuil psychoacoustique de l'AAC, qui ressortent après remontée. |
S (mix flottant et limiteur doux) à M (gain de capture) |
Moyen : changer le niveau enregistré est un choix produit. |
Compter les échantillons écrêtés. ebur128 avant/après (section 3.5). |
| 10 |
Synchro |
W, E |
Origine du curseur approximée : décalage = Date.now() à la réception de recording-started moins le départ du sampler (electron/ipc/handlers.ts:2893-2918). Le helper n'imprime cet événement qu'après une boucle de poll à 10 ms (main.cpp:1521-1525), plus la latence du pipe. Le sampler horodate en system_clock (cursor-sampler.cpp:41-44). Mesuré : télémétrie de 24,9 s pour 24,15 s de vidéo. |
Hypothèse : curseur décalé de 20 à 50 ms par rapport à l'image. Une seule horloge QPC le ramènerait sous 2 ms. |
M |
Faible. |
Prise avec curseur système visible et télémétrie : corréler les positions image par image. |
| 11 |
Perf + synchro |
W |
Audio WASAPI en polling, sans AUDCLNT_STREAMFLAGS_EVENTCALLBACK (wasapi_loopback_capture.cpp:249-255). Le qpcPosition est ignoré (:418). Les files micro et système n'ont pas de borne (audio_sample_utils.cpp:601). La dérive d'horloge entre périphérique et steady_clock n'est pas corrigée (plafond connu de #916). |
Moins de latence et de jitter. Prises longues sans latence qui grossit ni trous périodiques. |
M (événementiel) à L (rééchantillonnage adaptatif selon la profondeur de file) |
Moyen. |
Prise de 30 à 60 min avec bips réguliers : dérive mesurée en fin de fichier. |
| 12 |
Perf + qualité |
W |
Webcam copiée trois fois par frame, et à chaque tick d'écran : copyLatestFrame copie le frame entier avant de comparer la séquence (main.cpp:1173-1184, webcam_capture.cpp:622-636). S'y ajoutent latestFrame_.assign (:589) et captureNv12Sample (mf_encoder.cpp:1708). L'encodage webcam (WriteSample synchrone) tourne sur le fil de l'écran (main.cpp:1325-1334). |
Environ 190 Mo/s de copies inutiles en 1080p NV12 à 60 ticks/s, 750 Mo/s en 4K. Webcam et écran ne se ralentissent plus mutuellement. |
S (séquence d'abord) à M (fil dédié) |
Faible. |
storeMs et readSampleMs dans INFO: Webcam capture loop ended, plus CPU. |
| 13 |
Perf + latence |
W |
Pool WGC de 2 buffers, un frame pris par tick (wgc_session.cpp:250-254, :311), sans MinUpdateInterval. Sur l'écran à 279 Hz, WGC produit jusqu'à 279 frames/s pour 60 consommés. Hypothèse : le frame pris est le premier arrivé après le tick précédent, donc périmé de 13 à 27 ms. |
Moins de travail DWM et GPU. Image plus fraîche, meilleur accord avec l'audio et le curseur. |
S |
Faible. MinUpdateInterval exige Windows 11 24H2 (hypothèse sur l'interface exacte). |
Journaliser QPC maintenant - SystemRelativeTime à chaque frame pris. |
| 14 |
Perf |
W |
Device D3D créé sur l'adaptateur 0, pas celui de l'écran capturé (wgc_session.cpp:80-90). Lacune déjà documentée (recording.md, Known gaps). |
Portables hybrides : plus de copie inter-GPU à chaque frame. |
S à M |
Faible. |
Événement capture-adapter déjà émis (main.cpp:222+) : comparer les deux adaptateurs. |
| 15 |
Qualité (débit) |
M |
Débit toujours 76,5 Mbit/s, la valeur 4K60, quelle que soit la taille réelle (src/hooks/useScreenRecorder.ts:1401, ScreenCaptureRecorder.swift:758). Soit 4,25× le budget Windows en 1080p. |
Fichiers 2 à 4× plus petits sous 4K. |
S |
Faible. |
ffprobe du débit sur une prise de texte défilant, avant/après. |
| 16 |
Qualité + perf |
M |
Aucune couleur imposée (capture BGRA, ni colorSpaceName ni AVVideoColorPropertiesKey). Capture BGRA au lieu de 420v, queueDepth 6 (ScreenCaptureRecorder.swift:702-704). Environ 200 Mo de buffers en 4K. |
Teintes justes (P3 vers 709). Buffers ÷2,7. |
S |
Faible. Couleurs P3 hors gamut écrêtées. |
ffprobe -show_streams | grep color_, mire, footprint. |
| 17 |
Qualité |
M |
Intervalle de keyframe non fixé (ScreenCaptureRecorder.swift:753-788). Défaut VideoToolbox inconnu (hypothèse : GOP long). |
Scrub plus rapide dans l'éditeur. |
S |
Faible. |
Compter les K dans ffprobe -show_entries packet=flags. |
| 18 |
Mesurabilité |
M |
Frames refusés par l'encodeur jetés sans compteur (ScreenCaptureRecorder.swift:444-459). |
Rend mesurables tous les leviers macOS. |
S |
Nul. |
Compteurs émis à l'arrêt. |
| 19 |
Synchro audio |
L |
Le heartbeat peut être affamé : recv_timeout(tick) repart à chaque message, et les messages curseur n'appellent pas advance() (pipewire-capture/src/main.rs:679, :1161). Hypothèse sur le déclencheur : curseur à 144 Hz sur écran statique, donc ring audio de 2 s saturé et silence inséré. |
Pas de trou audio pendant qu'on bouge la souris. |
S |
Faible. |
Écran 144 Hz, souris en mouvement 10 s avec son : warning audio-dropped. |
| 20 |
Qualité + perf |
L |
Dmabuf aiguillé selon le succès de mmap, pas selon le modifier (pw_shim.c:1170-1211). Hypothèse : un buffer tuilé mappable (i915/xe) passe par le CPU et se brouille. Un buffer LINEAR en VRAM est relu sans cache. |
Image correcte sur Intel. Zéro-copie réellement utilisé. |
S à M |
Moyen. |
Intel iGPU, OPENSCREEN_PIPEWIRE_DEBUG=1. |
| 21 |
Perf |
L |
Frames tenus re-téléversés et ré-encodés (encoder.rs:646-667, capture.rs:571-578). Pas de maxFramerate demandé au compositeur (pw_shim.c:423-428). Tout sur un seul fil (main.rs:666-1257). |
CPU sur écran statique et écran 120/144 Hz. |
S |
Faible. |
encode_ms dans capture-stopped, CPU%. |
| 22 |
Robustesse |
L |
MP4 non fragmenté (encoder.rs:1070-1074) : un crash perd toute la prise. Jamais essayé sur Linux. |
Même protection que Windows et macOS. |
S |
Moyen : le seek du MP4 fragmenté est à mesurer. |
kill -9 en cours de prise, puis ffprobe. |
| 23 |
Perf |
L |
Pas de chemin matériel NVIDIA : échelle VAAPI, Vulkan, openh264 (encoder.rs:98), alors que h264_nvenc est dans le build vendu. |
Encodage matériel sur NVIDIA. |
M |
Moyen. |
fps réel et CPU sur carte NVIDIA. |
| 24 |
Qualité audio |
W, M |
Rééchantillonnage de mauvaise qualité pour les ratios non entiers. Windows : interpolation linéaire par paquet, sans état entre paquets (audio_sample_utils.cpp:404-433), par exemple micro 48 kHz vers sortie 44,1 kHz. macOS : plus proche voisin (AudioTrackMixer.swift:575-595). |
Moins d'aliasing et de clics aux bords de paquet. |
M |
Faible. |
Sinus 1 kHz via un périphérique à 44,1 kHz, FFT et THD+N. |
| 25 |
Qualité (débit) |
W |
Webcam 1080p à 16 Mbit/s plats (mesuré : 13,5 à 19,2 Mbit/s chaque seconde). Mesuré en réencodage : High à 8 Mbit/s donne 94,1 VMAF, Baseline à 16 Mbit/s 95,6, High à 16 Mbit/s 96,5. |
Avec le rang 3, environ 11 à 12 Mbit/s en High pour la qualité actuelle (interpolation, hypothèse). Environ 30 Mo/min économisés. |
S |
Faible, sauf capteurs bruités en basse lumière. |
VMAF sur prise réelle contre une référence haut débit. |
| 26 |
Durée de fin |
W |
Audio 0,44 à 0,58 s plus long que la vidéo sur 3 prises de 13 à 113 s (mesuré). Écart constant, donc pas de dérive, mais une queue : le mixeur tourne jusqu'à son étape d'arrêt, après la fin de la vidéo (main.cpp:1658-1690). |
Fichier qui finit net, sans demi-seconde d'image figée. |
S |
Faible. |
ffprobe stream=duration des deux pistes. |
| 27 |
Temps d'arrêt |
E |
Electron attend la sortie du process, pas l'événement recording-stopped (electron/recording/nativeWindowsCaptureStop.ts:333-376). Les WebM (sidecars macOS et Linux) sont réécrits en entier à l'arrêt (handlers.ts:518-528). Fenêtre éditeur créée à froid (handlers.ts:2378). |
Éditeur ouvert plus tôt. Hypothèse : plusieurs secondes sur une longue prise webcam en 4K. |
S à M |
Faible. |
Temps entre clic Stop et éditeur prêt. Lignes [stop-timing]. |
| 28 |
Robustesse |
E |
Pas de powerSaveBlocker pendant l'enregistrement (grep vide). Hypothèse : l'écran peut se mettre en veille pendant une longue prise sans interaction. |
Pas de capture noire ni de prise coupée. |
S |
Nul. |
Prise passive plus longue que le délai de veille. |
| 29 |
Qualité |
M |
Plafond 4K en boîte : un MacBook Pro 16" (3456×2234) est réduit à 3342×2160, facteur non entier (CaptureSizing.swift:62). |
Texte net en pixels natifs. |
S |
Faible (limite matérielle H.264 à vérifier). |
Taille dans recording-started, crop de texte. |
| 30 |
Produit |
Toutes |
Aucun réglage de qualité ni de cadence d'écran : 60 i/s codés en dur (useScreenRecorder.ts:35-38). Au-delà de 4096 px de large, les encodeurs H.264 matériels refusent (hypothèse) : un écran 5K tomberait sur le logiciel. |
Machines faibles à 30 i/s (#510). Écrans 5K encodables. |
M |
Faible. |
Sur machine faible : [pacing] à 30 contre 60. |
Base :
main@bc6fa483. Lecture seule du code, de l'historique git et GitHub, et des fichiers de%APPDATA%\openscreen\recordings. Aucun enregistrement lancé.Machine des mesures : RTX 4070 Ti, Ryzen 7 5800X, écran 1920×1080 à 279 Hz.
Légende. Vérifié : lu dans le code ou mesuré. Hypothèse : plausible, non prouvé, avec la mesure qui tranchera.
1. Synthèse
Conclusion. Le levier le moins cher et le plus rentable est la résolution du timer Windows. Elle dégrade déjà quatre flux mesurables : webcam, curseur, audio (#911) et cadence d'écran. Viennent ensuite trois corrections de réglages d'une ligne chacune (sélection du format webcam, profil H.264, balises couleur), puis la robustesse de la synchro A/V sous charge.
Les 5 leviers les plus rentables
wgc-captureetcursor-sampler. Gain : webcam à 30 i/s réguliers au lieu de 25,9 uniques et saccadés, curseur à 30 Hz au lieu de 22,7, paquets WASAPI moins en retard. Effort S. Risque faible.Ce qui ne vaut pas l'effort maintenant
2. Tableau classé des leviers
Effort : S (moins d'un jour), M (quelques jours), L (une semaine ou plus). Plateformes : W Windows, M macOS, L Linux, E Electron.
timeBeginPeriod, MMCSS ni priorité dans les helpers (grep vide). Toutes les attentes tombent sur le tick de 15,625 ms :main.cpp:1349(sleep_untildu writer),wasapi_loopback_capture.cpp:458(poll 5 ms),audio_sample_utils.cpp:717,cursor-sampler.cpp:407. Mesuré : intervalles webcam multiples de 15,625 ms à ±0,4 ms près ; curseur à 46-48 ms pour 33 demandés.SetProcessInformation(ProcessPowerThrottling)ou des timersCREATE_WAITABLE_TIMER_HIGH_RESOLUTION.analyze.js(section 3) : histogramme des intervalles webcam et curseur avant/après. Ligne[pacing].webcam_format.cpp:42-48,:69-78). Preset par défaut 2160p (src/hooks/webcamCaptureTarget.ts:42-48). Hypothèse forte : une C920 (YUY2 2304×1536 à 2 i/s, 1920×1080 à 5 i/s) est capturée à 2 i/s.webcam_format_test.cpp:42-45). Tester MJPEG vers NV12.INFO: Native webcam format WxH@Fdu helper sur une C920 ou C270.delivered=/ durée.MF_MT_MPEG2_PROFILE(mf_encoder.cpp:708-718). Mesuré en réencodant via le même MFT NVENC : High gagne +1,7 dB PSNR (écran) et +1,9 VMAF (webcam) à 4 Mbit/s.ffprobedoit lireHigh.sws_setColorspaceDetails(pipewire-capture/src/encoder.rs:758-789), aucune balise sur le codec (encoder.rs:347-373),scale_vaapisans matrice (dmabuf_import.rs:269-273). Le compositeur décode en BT.709 limité (crates/compositor/src/vk_shaders/layer.wgsl:82).color_range/space/primaries = unknown). Le chemin CPU efface les balises (mf_encoder.cpp:758-762,:770-778) et laisse le MFT choisir la matrice RGB32 vers YUV. Hypothèse : matrice non garantie BT.709 (NVENC et le Video Processor MFT peuvent choisir BT.601). Webcam : range et matrice balisés, mais primaries et transfer lus « reserved » (mesuré,mf_encoder.cpp:740-743ne pose niMF_MT_VIDEO_PRIMARIESniMF_MT_TRANSFER_FUNCTION).c9ff08acl'a prouvé : un blocage injecté de 300 ms ne laisse aucun trou. Or la boucle resynchronise sans rattraper (main.cpp:1341-1348). Chaque tick perdu décale donc toute la suite de la vidéo vers l'avant de l'audio, qui suit l'horloge.[pacing]: frames / (elapsed × fps).main.cpp:1258-1287). Chaque relecture faitCopyResourcepuisMapimmédiat, donc attente GPU (mf_encoder.cpp:1091-1094), plus un nouveau buffer de 8 Mo par frame (:1643-1646) et une copie ligne à ligne (:1106-1109). Estimation 1080p60 : environ 475 Mo/s par étape. 4K60 : environ 2 Go/s.typeperf "\Process(wgc-capture)\% Processor Time"et compteur GPU Engine, écran statique puis défilement, 60 s.main.cpp:925-928) depuis #336 : aucun repli si l'échec survient en cours de prise.OPENSCREEN_WGC_ENABLE_DXGI_INPUT=1.audio_sample_utils.cpp:459-467,:600). macOS :AudioTrackMixer.swift:609-613. Linux :capture.rs:137-142. Navigateur :src/lib/audioMix.ts:52-80. Mesuré : voix à -31,3 et -31,4 LUFS, crêtes à -12 et -15 dBFS. L'éditeur remonte ensuite jusqu'à +12 dB après l'AAC (PR #916).ebur128avant/après (section 3.5).Date.now()à la réception derecording-startedmoins le départ du sampler (electron/ipc/handlers.ts:2893-2918). Le helper n'imprime cet événement qu'après une boucle de poll à 10 ms (main.cpp:1521-1525), plus la latence du pipe. Le sampler horodate ensystem_clock(cursor-sampler.cpp:41-44). Mesuré : télémétrie de 24,9 s pour 24,15 s de vidéo.AUDCLNT_STREAMFLAGS_EVENTCALLBACK(wasapi_loopback_capture.cpp:249-255). LeqpcPositionest ignoré (:418). Les files micro et système n'ont pas de borne (audio_sample_utils.cpp:601). La dérive d'horloge entre périphérique etsteady_clockn'est pas corrigée (plafond connu de #916).copyLatestFramecopie le frame entier avant de comparer la séquence (main.cpp:1173-1184,webcam_capture.cpp:622-636). S'y ajoutentlatestFrame_.assign(:589) etcaptureNv12Sample(mf_encoder.cpp:1708). L'encodage webcam (WriteSamplesynchrone) tourne sur le fil de l'écran (main.cpp:1325-1334).storeMsetreadSampleMsdansINFO: Webcam capture loop ended, plus CPU.wgc_session.cpp:250-254,:311), sansMinUpdateInterval. Sur l'écran à 279 Hz, WGC produit jusqu'à 279 frames/s pour 60 consommés. Hypothèse : le frame pris est le premier arrivé après le tick précédent, donc périmé de 13 à 27 ms.MinUpdateIntervalexige Windows 11 24H2 (hypothèse sur l'interface exacte).QPC maintenant - SystemRelativeTimeà chaque frame pris.wgc_session.cpp:80-90). Lacune déjà documentée (recording.md, Known gaps).capture-adapterdéjà émis (main.cpp:222+) : comparer les deux adaptateurs.src/hooks/useScreenRecorder.ts:1401,ScreenCaptureRecorder.swift:758). Soit 4,25× le budget Windows en 1080p.ffprobedu débit sur une prise de texte défilant, avant/après.colorSpaceNameniAVVideoColorPropertiesKey). Capture BGRA au lieu de 420v,queueDepth6 (ScreenCaptureRecorder.swift:702-704). Environ 200 Mo de buffers en 4K.ffprobe -show_streams | grep color_, mire,footprint.ScreenCaptureRecorder.swift:753-788). Défaut VideoToolbox inconnu (hypothèse : GOP long).Kdansffprobe -show_entries packet=flags.ScreenCaptureRecorder.swift:444-459).recv_timeout(tick)repart à chaque message, et les messages curseur n'appellent pasadvance()(pipewire-capture/src/main.rs:679,:1161). Hypothèse sur le déclencheur : curseur à 144 Hz sur écran statique, donc ring audio de 2 s saturé et silence inséré.audio-dropped.pw_shim.c:1170-1211). Hypothèse : un buffer tuilé mappable (i915/xe) passe par le CPU et se brouille. Un buffer LINEAR en VRAM est relu sans cache.OPENSCREEN_PIPEWIRE_DEBUG=1.encoder.rs:646-667,capture.rs:571-578). Pas demaxFrameratedemandé au compositeur (pw_shim.c:423-428). Tout sur un seul fil (main.rs:666-1257).encode_msdanscapture-stopped, CPU%.encoder.rs:1070-1074) : un crash perd toute la prise. Jamais essayé sur Linux.kill -9en cours de prise, puisffprobe.encoder.rs:98), alors queh264_nvencest dans le build vendu.audio_sample_utils.cpp:404-433), par exemple micro 48 kHz vers sortie 44,1 kHz. macOS : plus proche voisin (AudioTrackMixer.swift:575-595).main.cpp:1658-1690).ffprobe stream=durationdes deux pistes.recording-stopped(electron/recording/nativeWindowsCaptureStop.ts:333-376). Les WebM (sidecars macOS et Linux) sont réécrits en entier à l'arrêt (handlers.ts:518-528). Fenêtre éditeur créée à froid (handlers.ts:2378).[stop-timing].powerSaveBlockerpendant l'enregistrement (grep vide). Hypothèse : l'écran peut se mettre en veille pendant une longue prise sans interaction.CaptureSizing.swift:62).recording-started, crop de texte.useScreenRecorder.ts:35-38). Au-delà de 4096 px de large, les encodeurs H.264 matériels refusent (hypothèse) : un écran 5K tomberait sur le logiciel.[pacing]à 30 contre 60.3. Mesures faites sur les fichiers existants
Fichiers :
%APPDATA%\openscreen\recordings. Le scriptanalyze.jsest en annexe.3.1 Conteneur, codec, couleur
recording-1790792371876.mp4…-webcam.mp4avg_frame_rate29,96, maisr_frame_rate60/1 : flux à cadence variable.tv, matricebt709, primaries et transferreserved.recording-1790546448062: AAC à 44,1 kHz (format de mix du périphérique).moof+mdatpour 24,7 s, surcoût 0,17 %.3.2 Cadence et GOP
Écran
Webcam
723 frames en 24,10 s.
Les intervalles sont des multiples de 15,625 ms, à ±0,4 ms près (tick du timer Windows) :
98 frames de moins de 1 Ko : doublons à 17 octets (13,6 %), soit 25,9 i/s uniques pour une caméra à 30.
Le commentaire de
main.cpp:1080-1089affirme que l'encodeur MF ignore les horodatages webcam. Ce n'est plus vrai sur le chemin NV12 matériel : ils sont conservés, avec la quantification du timer.3.3 Durées audio et vidéo
3.4 Profil High contre Baseline, même MFT matériel
Réencodage des deux fichiers via
h264_mf -hw_encoding 1(le MFT NVENC que le helper utilise), VBR non contraint, puis VMAF et PSNR contre le fichier enregistré.Limites
3.5 Niveaux audio
recording-1790546448062: 113 s de silence numérique total (-inf). Cause inconnue : rien joué, ou micro coupé. Aucun événement ne l'a signalé. macOS a unaudio-timelinequi l'aurait vu.3.6 Télémétrie curseur
4. Plan de mesure proposé
Ce qui demande une vraie prise. Chaque ligne donne la commande et la métrique qui prouve le gain.
4.1 Timer et MMCSS (rang 1)
analyze.js). Cible : plus de 95 % à 33 ms ± 2, doublons sous 2 %.[pacing]: frames / elapsed ≥ 59,5 à 60 i/s demandés.GetBuffer. Cible sous 12 ms.powercfg /energyliste les demandes de timer).4.2 Synchro A/V sous charge (rangs 6, 10, 26)
ffmpeg -i f.mp4 -vf signalstats,metadata=print:key=lavfi.signalstats.YAVG -f null -pour les instants de saut de luminance.ffmpeg -i f.mp4 -af astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.RMS_level -f null -pour les attaques.stressou compilation) et GPU. Le rang 6 est prouvé si l'écart croît sous charge avant le patch et reste plat après.4.3 Couleur (rangs 4, 5, 16)
Page : aplats #FF0000, #00FF00, #0000FF, #808080, #FFFFFF, sur les trois plateformes.
Lecture YUV brute :
ffmpeg -ss 2 -i f.mp4 -frames:v 1 -f rawvideo -pix_fmt yuv420p f.yuv, puis moyenne sur chaque aplat.Valeurs attendues en BT.709 limité
Signature BT.601 : rouge à Y≈81, Cb≈90.
Contrôle final : exporter, puis comparer les pixels RVB de l'export à la source (tolérance ±2).
4.4 Coût CPU et GPU (rangs 7, 8, 12, 13, 21)
typeperf "\Process(wgc-capture)\% Processor Time" "\GPU Engine(*engtype_3D)\Utilization Percentage" -si 1pendant 60 s.OPENSCREEN_WGC_ENABLE_DXGI_INPUT=1.QPC maintenant - SystemRelativeTimepar frame, sur l'écran à 279 Hz. Cible : médiane sous une demi-période.encode_mset le futurimport_ns. CPU% avec un compositeur à 144 Hz.powermetrics,footprinten 4K60.4.5 Webcam (rangs 2, 12, 25)
INFO: Native webcam format: cadence choisie. Cible ≥ 24 i/s.delivered=/ durée.analyze.js).4.6 Arrêt et prises longues (rangs 11, 27, 28)
[stop-timing]par étape, et temps entre clic Stop et éditeur interactif (chronomètre ou horodatages des logs[native-wgc]).5. Déjà tenté ou écarté
À ne pas refaire sans élément nouveau.
Windows
c9ff08ac). Avant :sleep_for(période)après le travail, donc 22 i/s pour 30 demandés. Garder l'échéance absolue. Le rang 6 complète ce commit, il ne le remplace pas.3847cf3b), après [Bug]: I tested version 1.10.0. #460 où tout était en logiciel.ICodecAPI, car les MFT matériels démarrent en CBR.8a82a2f9,58b2f086). Passé en opt-in (fix(wgc): make the GPU encode path opt-in until it earns the default #337,fe794ff1) après [Bug]: Error: Failed to encode WGC frame #336 : échec en cours de prise sans repli.7d0b0a7d) contre le blocage deCopyResource([Bug]: v1.8.0 native Windows recorder still hangs on stop; next attempt says capture is not running #252). Le callback reste disponible viaOPENSCREEN_WGC_LEGACY_FRAME_CALLBACK=1.WriteSample(fix(wgc): stop holding the frame mutex across blocking WriteSample calls #119,7e329594).a6795d23), avec repli vers le conteneur simple.7e6cde3f, fix(recording): advance the audio timeline on the clock, not on the data #406).d108bdd5).343c4078).d93a2b1a). Le RGB32 coûtait 92 ms par frame 4K.c71e69c6).webcam_format_test.cpp:42-45). Raison de l'exclusion actuelle, à retester en NV12.MinUpdateInterval, sélection d'adaptateur (lacune documentée).macOS
e1faaa461,4cdb215f4). Ne pas réactiver, ni les ajouter sur Windows sans le même test.a6795d23).df0427159).924a2cc26,2134b6bb3). Arrêt de la prise si le writer meurt (4ee0bff7f, [Bug]: macOS — the writer dies 75 s into a take, ScreenCaptureKit keeps delivering frames for 22 more minutes, and the HUD keeps counting #621 encore ouverte).714280677,51ad6b9a1,7faa2909a).b3d76aada,ece2d60aa).efe5accc5).Linux
THIRD-PARTY-NOTICES.md:23-25).d07b8e973).faststartrejeté : il réécrit tout le fichier.b2d1bb4ff). Le rattrapage compressait la vidéo d'environ 10 %.SPA_META_Header.pts) rejeté au profit de l'horloge du process.encoder.rs:358-371).PW_STREAM_FLAG_RT_PROCESSévité : le callback audio prend un mutex Rust.Transverse et export
decisions.md:39). Ne pas le transposer à la capture sans mesure.decisions.md:18). Tous les leviers ci-dessus la respectent.getDisplayMediasur Windows (decisions.md:19). Le chemin « helper absent » la contourne encore par un simpleconsole.warn(useScreenRecorder.ts:1193-1199).avg_frame_rate=30/1» (manual-e2e-checklist.md:195) échouerait sur la prise mesurée (29,96, cadence variable).Annexe :
analyze.jsAnalyse des paquets exportés par
ffprobe(intervalles, GOP, doublons, débit)Sous-issues : les leviers simples à fort gain, liées ci-dessous.