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:
| Artefatto | Regola di path attuale |
|---|---|
| MP4 | Path della registrazione completata, ad esempio <base>.mp4. |
| CSV | Stessa cartella e stesso base name dell'MP4, con suffisso _input.csv. |
| JSON | Stessa 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:
| Impostazione | Valore attuale | Effetto |
|---|---|---|
minVideoSizeBytes | 1,048,576 byte | I file MP4 sotto 1 MiB vengono bloccati come mp4_too_small. Il valore proviene da UploaderConfig quando disponibile. |
minVideoDurationSeconds | 1.0 secondo | Durata MP4 sotto 1 secondo viene bloccata come mp4_too_short. |
targetMuxedFps | 60.0 FPS | Target di prodotto usato per la diagnostica di validazione FPS. |
minMuxedFps | 60.0 FPS | Minimo nominale. |
minMuxedFpsAbsoluteTolerance | 2.0 FPS | Consente una tolleranza di 2 FPS dal target. |
minMuxedFpsTargetRatio | 0.95 | Consente un floor pari al 95% del target. |
| Floor effettivo degli FPS muxati | 58.0 FPS | Calcolato come min(60, max(60 - 2, 60 * 0.95)). |
durationToleranceSeconds | 5.0 secondi | Tolleranza minima sul mismatch di durata. |
durationToleranceRatio | 0.15 | Tolleranza relativa sul mismatch di durata. |
| Tolleranza di durata effettiva | max(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:
- Valida esistenza, status di regular-file, dimensione, contatori di frame, leggibilità della durata e durata minima dell'MP4.
- Valida esistenza, header, parsabilità, monotonia dei timestamp, JSON
event_args, boundary di lifecycle e durata effettiva positiva del CSV. - Valida esistenza e parsabilità del JSON. Se sono presenti campi di durata o di frame muxati, valida quei campi.
- Valida gli FPS muxati dal metadata JSON quando sia
duration_scheframe_count_muxedsono disponibili. - Confronta la durata CSV con la durata MP4.
- Confronta la durata JSON con la durata MP4 quando la durata JSON è disponibile.
- Ritorna
uploadablecon diagnosticamp4_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,576byte. - Le stats finali di capture non sono entrambe zero:
framesCaptured == 0 && framesEncoded == 0blocca comemp4_no_frames. - La durata MP4 è leggibile da
mvhd; se necessario, la durata di MP4 frammentato viene ricostruita dai datimoof/trun. - I payload
moovemoofoltre64 MiBvengono rigettati durante la lettura della durata. - La durata deve essere almeno
1.0secondo.
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 è:
| Campo | Comportamento attuale |
|---|---|
frame_count_muxed | Opzionale, ma se presente deve essere un intero maggiore di zero. Altrimenti il risultato è mp4_no_frames. |
duration_s | Opzionale, ma se presente deve essere finito e maggiore di zero. Altrimenti il risultato è json_duration_mismatch. |
start_unix_s + end_unix_s | Usati 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:
| Controllo | Valore attuale |
|---|---|
| Apertura esclusiva del file riesce | Richiesto |
| Età di stabilità del file | 2 secondi dall'ultima scrittura |
| Dimensione minima MP4 | 1,048,576 byte |
| Retry massimi | 3 |
| Limite di banda | 0 significa illimitato |
| Pausa durante la registrazione | true |
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:
| Ragione | Significato |
|---|---|
mp4_missing | Il path MP4 manca, non è un regular file o non può essere statt-ato. |
mp4_too_small | L'MP4 è più piccolo di minVideoSizeBytes, attualmente 1 MiB. |
mp4_no_frames | I contatori finali di capture e encoded sono zero, oppure JSON frame_count_muxed <= 0. |
mp4_duration_unreadable | La durata MP4 non può essere letta da mvhd o dai dati di durata frammentati. |
mp4_too_short | La durata MP4 è sotto 1.0 secondo. |
csv_missing | Il path CSV manca o non è un regular file. |
csv_parse_error | Il CSV non può essere aperto, è vuoto, ha l'header sbagliato, ha righe malformate, timestamp invalidi o JSON event_args invalido. |
csv_non_monotonic_timestamps | Un timestamp CSV è minore del timestamp della riga precedente. |
csv_missing_start | Non esiste né VIDEO_START né il fallback START. |
csv_missing_end | Non esiste né VIDEO_END né il fallback END. |
csv_duration_mismatch | La durata CSV è non positiva o differisce dall'MP4 oltre max(5s, 15%). |
json_missing | Il path del metadata JSON manca o non è un regular file. |
json_parse_error | Il JSON non può essere aperto o parsato. |
json_duration_mismatch | I 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.)