Skip to content

Étude : leviers d'optimisation de l'enregistrement (qualité et performance) #920

Description

@EtienneLescot

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

  1. Windows : timer 1 ms + MMCSS + sortie du bridage d'énergie dans wgc-capture et cursor-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.
  2. Windows : sélection du format webcam qui privilégie la cadence et accepte le MJPEG. Gain : évite une webcam USB 2 à 2 à 5 i/s avec le preset 2160p par défaut (hypothèse forte, C920). Effort S à M. Risque faible.
  3. Encodeurs : profil H.264 High (Windows) et débit ajusté à la taille réelle (macOS, webcam). Gain mesuré : +1,7 dB PSNR (écran) et +1,9 VMAF (webcam) à débit égal. macOS : fichiers 2 à 4× plus petits sous 4K. Effort S. Risque faible.
  4. Couleur : conversion et balises BT.709 limité sur les trois plateformes. Le compositeur décode tout en BT.709 limité. Linux convertit en BT.601 sans balise (vérifié dans le code). Windows écrit la piste écran sans aucune balise (mesuré). Gain : teintes exactes à l'export. Effort S par plateforme. Risque faible.
  5. Windows : combler les ticks manqués et ne plus relire un frame inchangé. Le sink writer renumérote la vidéo écran en CFR (mesuré), donc chaque tick perdu sous charge avance toute la vidéo par rapport à l'audio. Gain : synchro A/V tenue sur machine lente ([Bug]: Recorded video game is not played properly #510), relecture GPU évitée sur écran statique. Effort S à M. Risque faible.

Ce qui ne vaut pas l'effort maintenant

  • HEVC : décodage 4K 8× plus lent côté compositeur macOS.
  • B-frames : déjà rejetées sur macOS (offsets négatifs avec les fragments).
  • GOP plus long : 19 % des octets en keyframes, mais le scrub de l'éditeur en dépend.

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.

Rang Axe Plateforme Constat et preuve Gain attendu Effort Risque Comment le mesurer
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.

3. Mesures faites sur les fichiers existants

Fichiers : %APPDATA%\openscreen\recordings. Le script analyze.js est en annexe.

3.1 Conteneur, codec, couleur

ffprobe -v error -show_entries format=duration,bit_rate -show_entries stream=codec_name,profile,level,pix_fmt,width,height,r_frame_rate,avg_frame_rate,bit_rate,color_range,color_space,color_transfer,color_primaries,has_b_frames,refs,sample_rate,channels -of compact <fichier>
  • Écran recording-1790792371876.mp4
    • H.264 Constrained Baseline, level 4.2, refs=1, sans B-frames.
    • 1920×1032, 60/1, 8,39 Mbit/s en moyenne pour un budget VBR de 18 Mbit/s.
    • Couleur : unknown partout.
    • AAC LC, 48 kHz, stéréo, 192 kbit/s.
  • Webcam …-webcam.mp4
    • H.264 Constrained Baseline, level 4.0, 1920×1080, 15,95 Mbit/s.
    • avg_frame_rate 29,96, mais r_frame_rate 60/1 : flux à cadence variable.
    • Couleur : range tv, matrice bt709, primaries et transfer reserved.
  • Anciennes prises
  • Fragmentation (parcours des boîtes MP4)
    • Écran : 25 moof + mdat pour 24,7 s, surcoût 0,17 %.
    • Webcam : surcoût 0,03 %.
    • Confirme des fragments d'une seconde à coût négligeable.

3.2 Cadence et GOP

ffprobe -v error -select_streams v:0 -show_entries packet=pts_time,dts_time,duration_time,size,flags -of csv=p=0 <fichier> > packets.csv
node analyze.js packets.csv

Écran

  • 1449 frames en 24,13 s.
  • Tous les intervalles valent exactement 16,667 ms : CFR renumérotée, les horodatages WGC ne survivent pas.
  • GOP de 1 s : 25 keyframes. Elles pèsent 19,4 % des octets vidéo.
  • 7,9 % de frames de moins de 1 Ko (écran inchangé).
  • Débit par seconde : de 2,5 à 24,5 Mbit/s. Le VBR fonctionne.

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) :

    Intervalle (ticks de 15,625 ms) 0 1 2 3 4
    Nombre 11 34 527 142 8
  • 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-1089 affirme 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

ffprobe -v error -count_packets -show_entries stream=codec_type,duration,nb_read_packets -of compact <fichier>
Prise Vidéo Audio Écart
1790792371876 24,150 s 24,725 s +0,575 s
1790426899927 12,567 s 13,013 s +0,447 s
1790546448062 112,783 s 113,220 s +0,437 s
  • L'écart est constant, pas proportionnel à la durée. Pas de dérive progressive sur cette machine rapide.
  • C'est une queue audio au début ou à la fin de la prise. Le code pointe la fin (rang 26).

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é.

ffmpeg -i <src> -an -fps_mode passthrough -pix_fmt nv12 -c:v h264_mf -hw_encoding 1 -profile:v 66|100 -rate_control u_vbr -b:v 4M -g 60 out.mp4
ffmpeg -i out.mp4 -i <src> -lavfi "[0:v]settb=1/1000,setpts=N[d];[1:v]settb=1/1000,setpts=N[r];[d][r]libvmaf=n_threads=16:n_subsample=3" -f null -
Source Réglage Débit réel VMAF PSNR
Écran Baseline 4M 4,13 Mbit/s 96,15 45,31 dB
Écran High 4M 4,04 Mbit/s 96,56 46,98 dB
Webcam Baseline 4M 4,14 Mbit/s 89,11 n/a
Webcam High 4M 4,14 Mbit/s 91,05 n/a
Webcam High 8M 8,04 Mbit/s 94,14 n/a
Webcam Baseline 16M ~16 Mbit/s 95,64 n/a
Webcam High 16M 15,75 Mbit/s 96,48 n/a

Limites

  • Mesure générationnelle : la référence est déjà compressée.
  • Le helper donne du RGB32 au MFT, ffmpeg du NV12.
  • Le PSNR webcam est inutilisable : cadence variable et alignement des frames.
  • Le sens de l'écart est net. Son ampleur exacte est à confirmer sur une source propre.

3.5 Niveaux audio

ffmpeg -i <f> -map 0:a:0 -af ebur128=peak=true -f null -
ffmpeg -i <f> -map 0:a:0 -af astats=measure_overall=Peak_level+RMS_level+Peak_count -f null -
  • Prises voix : -31,3 et -31,4 LUFS intégrés, crêtes à -12,3 et -14,8 dBFS. Aucun écrêtage sur ces prises. Niveau environ 15 dB sous la cible habituelle de -16 LUFS.
  • 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 un audio-timeline qui l'aurait vu.

3.6 Télémétrie curseur

node -e "<intervalles de samples[].timeMs>" recording-1790792371876.mp4.cursor.json
  • 567 échantillons en 24,9 s, soit 22,7 Hz (33 ms demandés, donc 30 Hz visés).
  • Intervalles : médiane 47 ms, 45 à 48 ms. C'est 3 ticks de 15,625 ms.
  • La télémétrie couvre 24,9 s, contre 24,15 s de vidéo.

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)

  • Protocole : même prise de 60 s, webcam et micro actifs, helper actuel contre helper patché.
  • Métriques
    • Histogramme des intervalles webcam (analyze.js). Cible : plus de 95 % à 33 ms ± 2, doublons sous 2 %.
    • Fréquence curseur. Cible : 30 Hz ± 1.
    • Ligne [pacing] : frames / elapsed ≥ 59,5 à 60 i/s demandés.
    • Retard des paquets WASAPI : journaliser l'écart max entre deux GetBuffer. Cible sous 12 ms.
  • Contrôle Windows 11 : relancer avec la fenêtre de l'app minimisée, et vérifier que la résolution demandée tient (powercfg /energy liste les demandes de timer).

4.2 Synchro A/V sous charge (rangs 6, 10, 26)

  • Page de test : un flash plein écran blanc et un bip de 20 ms toutes les 2 s. À enregistrer sur la cible.
  • Mesures dans le fichier
    • Image : ffmpeg -i f.mp4 -vf signalstats,metadata=print:key=lavfi.signalstats.YAVG -f null - pour les instants de saut de luminance.
    • Son : ffmpeg -i f.mp4 -af astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.RMS_level -f null - pour les attaques.
    • Métrique : écart image moins son pour chaque paire, en début et en fin de prise.
  • Conditions : repos, puis charge CPU (stress ou compilation) et GPU. Le rang 6 est prouvé si l'écart croît sous charge avant le patch et reste plat après.
  • Curseur : même prise avec curseur système capturé et télémétrie. Corréler la position détectée dans l'image avec la télémétrie. Métrique : décalage en ms.

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é

    Aplat Y Cb Cr
    Rouge 63 102 240
    Vert 173 42 26
    Bleu 32 240 118
  • 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)

  • Windows
    • typeperf "\Process(wgc-capture)\% Processor Time" "\GPU Engine(*engtype_3D)\Utilization Percentage" -si 1 pendant 60 s.
    • Trois scènes : écran statique, défilement de texte, vidéo plein écran.
    • En 1080p60 et en 4K60 si possible.
    • Chemin CPU contre OPENSCREEN_WGC_ENABLE_DXGI_INPUT=1.
  • Fraîcheur WGC (rang 13) : journaliser QPC maintenant - SystemRelativeTime par frame, sur l'écran à 279 Hz. Cible : médiane sous une demi-période.
  • Linux : encode_ms et le futur import_ns. CPU% avec un compositeur à 144 Hz.
  • macOS : compteurs de frames refusés (rang 18), powermetrics, footprint en 4K60.

4.5 Webcam (rangs 2, 12, 25)

  • Caméras : une C920 ou C270 (USB 2) et la StreamCam actuelle. Presets 1080p et 2160p.
  • Métriques
    • Ligne INFO: Native webcam format : cadence choisie. Cible ≥ 24 i/s.
    • delivered= / durée.
    • Frames uniques/s via la taille des paquets (analyze.js).
  • Débit (rang 25) : prise de référence à 40 Mbit/s en High, puis VMAF des variantes 8, 12 et 16 Mbit/s contre elle.

4.6 Arrêt et prises longues (rangs 11, 27, 28)

  • Arrêt : prise de 10 min, puis Save Diagnostics.
    • Métriques : lignes [stop-timing] par étape, et temps entre clic Stop et éditeur interactif (chronomètre ou horodatages des logs [native-wgc]).
  • Dérive : prise de 60 min avec bips toutes les 30 s.
    • Métrique : écart entre le temps attendu et le temps mesuré de chaque bip. Taille des files du mixeur, journalisée chaque minute.
  • Veille : prise passive de 20 min avec un délai de veille écran de 5 min.

5. Déjà tenté ou écarté

À ne pas refaire sans élément nouveau.

Windows

macOS

Linux

Transverse et export


Annexe : analyze.js

Analyse des paquets exportés par ffprobe (intervalles, GOP, doublons, débit)
const fs=require('fs');
for (const f of process.argv.slice(2)) {
  const rows=fs.readFileSync(f,'utf8').trim().split('\n').map(l=>l.split(','));
  const pts=rows.map(r=>+r[0]), size=rows.map(r=>+r[3]), key=rows.map(r=>r[4].startsWith('K'));
  const d=[]; for(let i=1;i<pts.length;i++) d.push((pts[i]-pts[i-1])*1000);
  const s=[...d].sort((a,b)=>a-b); const q=p=>s[Math.floor(p*(s.length-1))];
  const hist={}; for(const x of d){const b=Math.round(x); hist[b]=(hist[b]||0)+1;}
  const top=Object.entries(hist).sort((a,b)=>b[1]-a[1]).slice(0,8).map(([k,v])=>k+'ms:'+v).join(' ');
  const keys=pts.filter((_,i)=>key[i]); const gops=[]; for(let i=1;i<keys.length;i++) gops.push(+(keys[i]-keys[i-1]).toFixed(2));
  const tiny=size.filter(x=>x<1000).length; const tot=size.reduce((a,b)=>a+b,0);
  const keyBytes=size.filter((_,i)=>key[i]).reduce((a,b)=>a+b,0);
  const dur=pts[pts.length-1]-pts[0];
  console.log(`== ${f}\n frames=${pts.length} dur=${dur.toFixed(2)}s fps_eff=${(pts.length/dur).toFixed(2)}\n interval ms: min=${q(0).toFixed(2)} p5=${q(.05).toFixed(2)} p50=${q(.5).toFixed(2)} p95=${q(.95).toFixed(2)} p99=${q(.99).toFixed(2)} max=${q(1).toFixed(2)}\n top intervals: ${top}\n gaps>2x nominal: ${d.filter(x=>x>2*s[Math.floor(s.length/2)]).length}\n keyframes=${keys.length} GOP(s)=${gops.slice(0,12).join(',')}${gops.length>12?'...':''}\n tiny(<1KB) frames=${tiny} (${(100*tiny/pts.length).toFixed(1)}%)  keyframe bytes=${(100*keyBytes/tot).toFixed(1)}% of video  avg kbps=${(tot*8/dur/1000).toFixed(0)}`);
  // bitrate per second
  const per={}; pts.forEach((p,i)=>{const k=Math.floor(p); per[k]=(per[k]||0)+size[i];});
  console.log(' kbps per second: '+Object.values(per).map(b=>Math.round(b*8/1000)).join(' '));
}

Sous-issues : les leviers simples à fort gain, liées ci-dessous.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions