Pipeline di capture Pure-v2
Pure-v2 è il percorso di registrazione attuale. Cattura i frame, codifica il video, fa il muxing di audio e video in MP4, scrive gli eventi CSV ed emette metadata JSON.
Pipeline
Invarianti verificate
Queste proprietà sono state stabilite leggendo il percorso di codice di produzione e verificandole a mano durante una review di affidabilità (EC-282) e la rimozione di codice morto che è seguita (EC-283). Sono documentate qui perché ognuna è un fatto non ovvio che una lettura superficiale del sorgente — o uno scan automatico — tende a interpretare male, segnalando un bug che non esiste. Trattale come assiomi controllati: se pensi di aver trovato un difetto che contraddice una di queste, rileggi il codice citato prima di aprire una segnalazione.
- WGC gira sempre in queue mode in produzione.
DesktopCapturechiamaenableQueueMode(true)incondizionatamente prima di avviare la capture (desktop_capture.cpp). Conseguenze:- L'unico produttore di frame è il polling thread (
WindowsGraphicsCapture::capturePollingThreadFunc→enqueueFrame). Il vecchio path event-drivenonFrameArrivednon era mai abilitato in produzione ed è stato rimosso (EC-283), insieme al meccanismo single-textureprocessFrameWithZeroCopy/zeroCopyTexture_(soppiantato dal pool) e al readback CPUprocessFrameWithStaging. Lo zero-copy GPU→NVENC vive ora esclusivamente nel path a pool (createFrameTextures→cudaResources_). - Poiché c'è esattamente un produttore e un consumatore, la CaptureQueue lock-free è una SPSC ring genuina — l'assunzione single-producer/single-consumer è rispettata, non violata.
- L'unico produttore di frame è il polling thread (
- WGC registra una finestra, mai uno schermo. Playroll cattura sempre la finestra di gioco rilevata (
CaptureManager::startCapture(HWND)→startWindowStreaming→startWindowCapture). Il path di cattura monitor intero (startDisplayCapture/setMonitor/startDisplayStreaming) non aveva chiamanti ed è stato rimosso (EC-283).WGC::initializenon pre-crea più alcun capture item;startWindowCaptureè l'unico creatore (il capture item monitor costruito all'init veniva sempre scartato). - Il timing video è CFR ancorato al wall-clock, non CFR "conteggio-frame-codificati". Il PTS video muxato è
frame_index * (90000 / fps), maframe_indexavanza per corrispondere al tempo wall-clock trascorso (frame count target derivato dai microsecondi trascorsi), epadPureV2CfrTailfa padding della coda per mantenerlo allineato. I frame droppati/decimati quindi non accorciano il video rispetto al sample clock audio — l'A/V resta sincronizzato. (È robusto ma dipende dal fatto che il padding CFR venga sempre eseguito; quel path è load-bearing.) open_flagimplicaheader_writtennel muxer. InHybridMp4Muxer,header_writtenè impostato immediatamente prima diopen_flag, eclose_clean()ritorna subito seopen_flagnon è impostato. Quindiav_write_trailernon viene mai raggiunto con l'header non scritto, e il path di chiusura brusca salta del tutto il trailer. I due flag sono logicamente accoppiati, non indipendenti.- La catena RAII NVENC/CUDA è bilanciata anche durante il recovery mid-session.
CudaContext→NvidiaNvsdkV2→ (sessione encoder, risorse registrate, bitstream buffer) sono rilasciati nell'ordine di dipendenza corretto, anche attraverso un ciclo di recovery destroy+reinit. Lock/unlock del bitstream e map/unmap delle risorse sono accoppiati su tutti i path. - I pacchetti codificati sono spostati (move), non copiati, lungo tutta la pipeline — encoder →
EncodeStage→PacketQueue→ mux stage usano tuttistd::move, e l'ultimo hop di mux passa il buffer a FFmpeg viaav_buffer_create(zero-copy) invece diav_new_packet+ memcpy (EC-282).
Sincronizzazione delle scritture di gioco/input
Pure-v2 non blocca l'encoder video mentre scrive le righe di input su disco. La sincronizzazione è logica e avviene tramite ancoraggi di sessione condivisi:
| Ancoraggio | Sorgente | Scopo |
|---|---|---|
frame_number | CaptureManager::currentInputFrameNumber() | Collega le righe di lifecycle e input alla posizione attuale del frame codificato. In pure-v2 proviene da framesEncodedAtomic_. |
timestamp | std::chrono::system_clock::now() al momento dell'osservazione dell'evento | Fornisce ordinamento assoluto e calcolo delle durate sulle righe di lifecycle CSV. |
| Righe di lifecycle | START, VIDEO_START, VIDEO_PAUSE, VIDEO_RESUME, VIDEO_END, END | Delimita l'intervallo video effettivo e gli intervalli di pausa usati dalla validazione. |
| Coda asincrona | Thread writer di InputEventRecorder | Disaccoppia la persistenza degli input dal throughput di capture/encode. |
La proprietà importante è che le scritture di capture e quelle di input non devono essere fisicamente sincrone. Devono condividere abbastanza ancoraggi da poter ricostruire lo stesso intervallo di registrazione durante la validazione.
A livello operativo:
- Il percorso MP4 detiene l'ordinamento e la cadenza dei sample video. Il writer CSV non si trova mai sull'hot path di encode/mux.
- Gli eventi di input e di lifecycle sono timestamp-ati prima di entrare nella coda asincrona, quindi il ritardo del writer non altera il loro tempo di evento.
VIDEO_STARTviene emesso dopo che la registrazione CSV è partita, eVIDEO_ENDviene emesso prima chestopRecording()esegua il flush finale del CSV.- Il validator analizza i timestamp di lifecycle CSV, richiede timestamp monotoni e
event_argsJSON validi, sottrae gli intervalli di pausa, poi confronta la durata risultante del CSV con la durata MP4/JSON.
Sotto pressione della coda, le righe di lifecycle vengono preservate con priorità e le righe di input non-lifecycle sono le prime candidate al drop. Questo mantiene il CSV utilizzabile per la validazione dell'upload e per il troubleshooting dei tester anche quando la fedeltà di dettaglio dell'input è degradata.
Qualità target
L'obiettivo di prodotto è 60 FPS. Il minimo effettivo compatibile col validator è attualmente 58 FPS per un target di 60 FPS. Questo lascia tolleranza per il rumore di timing pur respingendo registrazioni che mancano materialmente il target di qualità.
Profiling FPS
Il core include un modulo FPSProfiler che osserva snapshot QoS periodici di pure-v2.
Calcola:
- FPS sorgente nella finestra.
- FPS codificati nella finestra.
- FPS muxati nella finestra.
- FPS muxati cumulativi.
- Rapporto di drop.
- FPS muxati peggiori della finestra mentre un warning è attivo.
Emette eventi quando la qualità degrada, escala o si ripristina.
Ciclo di vita del frame encoder
Ownership della backpressure
Ragioni di warning
| Ragione | Significato |
|---|---|
low_source_fps | Il lato sorgente/capture non sta producendo abbastanza frame. |
capture_pool_pressure | La pressione sul texture pool o i drop di slot del pool stanno causando perdita di frame. |
encode_backpressure | La latenza dell'encoder o le attese di lock su NVENC sono troppo alte. |
mux_backpressure | Coda dei pacchetti, mux o I/O su disco stanno rallentando la pipeline. |
low_muxed_fps | Gli FPS muxati sono bassi e non è stata identificata una causa più specifica. |
Evento attuale
capture.v2.fps_profiler.event
Questo evento viene loggato dal core oggi. L'esposizione UI è uno step di integrazione separato.
Fallimenti monitorati correlati
Pure-v2 emette telemetria anche per condizioni che possono spiegare registrazioni fallite o degradate:
| Famiglia di eventi | Significato |
|---|---|
capture.v2.qos* | Pressione di coda, frame persi e backpressure di capture. |
capture.v2.pool_pressure | Pressione sul texture pool e contesa di risorse GPU. |
capture.v2.encode_stage.frame_metrics | Throughput e latenza di encode. |
capture.v2.packet_queue.depth | Crescita della coda pacchetti e blocchi di enqueue. |
capture.v2.mux_stage.write_metrics | Latenza di scrittura MP4 e I/O su disco. |
capture.v2.zero_video_output_watchdog | Pipeline attiva senza output video muxato. |
capture.v2.nvenc.* | Warning e fallimenti dell'encoder NVIDIA. |
Vedere Core Signals per il catalogo completo dei segnali user-facing e operativi.