Passa al contenuto principale

Pipeline di validazione degli upload

La validazione degli upload protegge la consistenza dei dati e l'accuratezza dei pagamenti. Una sessione dovrebbe caricare solo dopo che gli artefatti locali dimostrano che la registrazione è coerente.

L'implementazione attuale vive in RecordingArtifactValidator ed è chiamata da RecordingSessionFinalizer prima che i file siano registrati per l'upload.

Artefatti richiesti

Le sessioni pure-v2 richiedono questa tripletta locale:

ArtefattoRegola di path attuale
MP4Path della registrazione completata, ad esempio <base>.mp4.
CSVStessa cartella e stesso base name dell'MP4, con suffisso _input.csv.
JSONStessa cartella e stesso base name dell'MP4, con suffisso _meta.json.

L'audio è multiplexato nell'MP4 come AAC. Non c'è più un sidecar audio separato — l'artefatto legacy _audio.ogg è stato rimosso in EC-173 (commit af48e44, 2026-05-06) e il finalizer non lo referenzia più.

Config di validazione attuale

Questi sono i valori di default oggi nel codice:

ImpostazioneValore attualeEffetto
minVideoSizeBytes1,048,576 byteI file MP4 sotto 1 MiB vengono bloccati come mp4_too_small. Il valore proviene da UploaderConfig quando disponibile.
minVideoDurationSeconds1.0 secondoDurata MP4 sotto 1 secondo viene bloccata come mp4_too_short.
targetMuxedFps60.0 FPSTarget di prodotto usato per la diagnostica di validazione FPS.
minMuxedFps60.0 FPSMinimo nominale.
minMuxedFpsAbsoluteTolerance2.0 FPSConsente una tolleranza di 2 FPS dal target.
minMuxedFpsTargetRatio0.95Consente un floor pari al 95% del target.
Floor effettivo degli FPS muxati58.0 FPSCalcolato come min(60, max(60 - 2, 60 * 0.95)).
durationToleranceSeconds5.0 secondiTolleranza minima sul mismatch di durata.
durationToleranceRatio0.15Tolleranza relativa sul mismatch di durata.
Tolleranza di durata effettivamax(5s, reference_duration * 0.15)reference_duration è la maggiore delle durate assolute in confronto.

Ordine di validazione

Il validator si interrompe al primo fallimento bloccante:

  1. Valida esistenza, status di regular-file, dimensione, contatori di frame, leggibilità della durata e durata minima dell'MP4.
  2. Valida esistenza, header, parsabilità, monotonia dei timestamp, JSON event_args, boundary di lifecycle e durata effettiva positiva del CSV.
  3. Valida esistenza e parsabilità del JSON. Se sono presenti campi di durata o di frame muxati, valida quei campi.
  4. Valida gli FPS muxati dal metadata JSON quando sia duration_s che frame_count_muxed sono disponibili.
  5. Confronta la durata CSV con la durata MP4.
  6. Confronta la durata JSON con la durata MP4 quando la durata JSON è disponibile.
  7. Ritorna uploadable con diagnostica mp4_csv_json_valid.

Solo dopo questo successo RecordingSessionFinalizer registra i tre file locali in SQLite come mp4, csv e json.

Controlli MP4

Il gate MP4 attualmente valida:

  • Il path esiste ed è un regular file.
  • La dimensione del file è almeno 1,048,576 byte.
  • Le stats finali di capture non sono entrambe zero: framesCaptured == 0 && framesEncoded == 0 blocca come mp4_no_frames.
  • La durata MP4 è leggibile da mvhd; se necessario, la durata di MP4 frammentato viene ricostruita dai dati moof/trun.
  • I payload moov e moof oltre 64 MiB vengono rigettati durante la lettura della durata.
  • La durata deve essere almeno 1.0 secondo.

Controlli CSV

Il contratto CSV è esatto:

frame_number,timestamp,event_type,event_args

Le righe devono avere esattamente quattro campi. timestamp deve parsare come numero finito e deve essere monotono; timestamp uguali sono consentiti, timestamp decrescenti vengono bloccati. event_args deve parsare come JSON quando presente.

La durata di lifecycle usa VIDEO_START e VIDEO_END quando entrambi esistono. Se mancano, il validator ricade su START e END. Gli intervalli VIDEO_PAUSE e VIDEO_RESUME tra VIDEO_START e VIDEO_END vengono sottratti dalla durata CSV. Se il CSV è in pausa quando arriva VIDEO_END, l'intervallo di pausa finale viene sottratto fino a VIDEO_END.

La durata CSV risultante deve essere positiva.

Controlli JSON

Il file JSON è obbligatorio e deve parsare. Il comportamento attuale dei campi è:

CampoComportamento attuale
frame_count_muxedOpzionale, ma se presente deve essere un intero maggiore di zero. Altrimenti il risultato è mp4_no_frames.
duration_sOpzionale, ma se presente deve essere finito e maggiore di zero. Altrimenti il risultato è json_duration_mismatch.
start_unix_s + end_unix_sUsati come fallback per la durata quando duration_s è assente. end_unix_s deve essere maggiore di start_unix_s.

La validazione degli FPS muxati gira solo quando il JSON ha una durata valida e frame_count_muxed > 0. Il valore calcolato è:

muxed_fps = frame_count_muxed / duration_s

Da EC-360 questo controllo è solo osservativo: quando muxed_fps < 58.0 la condizione viene aggiunta alla diagnostica di validazione come mp4_fps_below_threshold (observe-only) ed emerge nella telemetria recording.validation, ma la sessione resta caricabile. (Fino alla 4.1.10 bloccava l'upload, e EC-346 cancellava poi il filmato; il feedback dal campo ha rovesciato quella scelta.) La suddivisione onesta dei frame real-vs-CFR è registrata nel blocco fps_breakdown del _meta.json, così le sessioni di bassa qualità vengono filtrate a valle.

Controlli di readiness dell'uploader

Dopo la validazione e la registrazione in SQLite, l'uploader verifica ancora la readiness del file prima del trasferimento:

ControlloValore attuale
Apertura esclusiva del file riesceRichiesto
Età di stabilità del file2 secondi dall'ultima scrittura
Dimensione minima MP41,048,576 byte
Retry massimi3
Limite di banda0 significa illimitato
Pausa durante la registrazionetrue

Questi controlli dell'uploader non sostituiscono il validator della tripletta. Proteggono solo la readiness del trasferimento.

Ragioni di fallimento attuali

Questi sono i valori reason effettivamente ritornati dal validator oggi:

RagioneSignificato
mp4_missingIl path MP4 manca, non è un regular file o non può essere statt-ato.
mp4_too_smallL'MP4 è più piccolo di minVideoSizeBytes, attualmente 1 MiB.
mp4_no_framesI contatori finali di capture e encoded sono zero, oppure JSON frame_count_muxed <= 0.
mp4_duration_unreadableLa durata MP4 non può essere letta da mvhd o dai dati di durata frammentati.
mp4_too_shortLa durata MP4 è sotto 1.0 secondo.
csv_missingIl path CSV manca o non è un regular file.
csv_parse_errorIl CSV non può essere aperto, è vuoto, ha l'header sbagliato, ha righe malformate, timestamp invalidi o JSON event_args invalido.
csv_non_monotonic_timestampsUn timestamp CSV è minore del timestamp della riga precedente.
csv_missing_startNon esiste né VIDEO_START né il fallback START.
csv_missing_endNon esiste né VIDEO_END né il fallback END.
csv_duration_mismatchLa durata CSV è non positiva o differisce dall'MP4 oltre max(5s, 15%).
json_missingIl path del metadata JSON manca o non è un regular file.
json_parse_errorIl JSON non può essere aperto o parsato.
json_duration_mismatchI campi di durata JSON sono invalidi o differiscono dall'MP4 oltre max(5s, 15%).

mp4_fps_below_threshold non è più una ragione di blocco: da EC-360 un muxed FPS derivato dal JSON sotto il floor di 58.0 viene registrato nella diagnostica di un risultato comunque caricabile (vedi "Controlli JSON" sopra).

Invariante importante

Gli upload parziali non sono accettabili per le nuove sessioni. Se la validazione ritorna blocked, nessun file MP4, CSV o JSON deve essere registrato per l'upload.

Per una sessione pure-v2 valida, i record di upload lato server devono corrispondere al numero richiesto di artefatti: tre record di upload per sessione caricabile.

Se la validazione fallisce, la sessione viene marcata come failed con la ragione granulare di blocco. Se il fallimento è mp4_no_frames, oppure le stats finali mostrano nessun frame, il finalizer scarta il metadata locale MP4, CSV e JSON. Quello è l'unico caso di cancellazione: ogni altro artefatto bloccato resta su disco per ispezione e recovery. (EC-346 / 4.1.10 aveva esteso brevemente la cancellazione alla famiglia low-fps / under-delivered — mp4_fps_below_threshold, csv_duration_mismatch, mp4_too_short; EC-360 ha annullato quella scelta dopo il feedback dal campo.)