ADR 0002: Chunk Videos Client-side Before Upload
- Status: Superseded / deferred
- Date: 2025-10-03
- Superseded on: 2026-05-08
- Deciders: Akshay
- Tags: frontend, pipeline, ux
Contextโ
Users record long practice sessions (15-60 minutes). A single 60-minute 1080p video is ~5 GB. Uploading that as one blob is hostile:
- Mobile uploads are unreliable past ~500 MB โ partial failures are frequent
- GPU processing a 60-min video on one worker takes > 10 min โ users stare at a spinner
- Memory spikes on RTX 4090 at long durations โ risk of OOM
- Retrying a failure means re-uploading the whole thing
Options Consideredโ
A. Upload whole video, chunk server-sideโ
- Pros: Simple client; no FFmpeg on device
- Cons: User waits for the whole upload before processing starts; mobile network unreliability; extra GCS egress when Functions re-download the blob
B. Resumable upload with server-side chunkingโ
- Pros: Partial-failure recovery; single upload URL
- Cons: Still serial processing; doesn't help GPU OOM; more server complexity
C. Client-side chunking + parallel uploadโ
- Pros: Processing can start on chunk 1 while chunk 2 is still uploading; per-chunk retry; bounded GPU memory; better user perception of progress
- Cons: Requires FFmpeg on device (adds ~40 MB to Flutter bundle); merge logic on the server; overlap handling for events straddling chunk boundaries
Current Production Stateโ
This ADR described the intended chunking architecture, but the shipped Flutter
upload path currently sends a single video to the requestUploadPath /
processVideoOnUpload pipeline. The backend still keeps sessionId,
chunkIndex, totalChunks, isChunked, mergeAnalyses, and
mergeChunkResults so the architecture can support chunking later.
requestUploadPath creates one job with chunkIndex: 0, totalChunks: 1,
and isChunked: false; the client does not ship FFmpeg-based splitting.
Until the client-side FFmpeg implementation is actually wired and verified, the launch contract is: one uploaded video produces one post-match report.
Original Decisionโ
Chunk videos client-side into 5-minute segments with 3-second overlap, using FFmpeg FFI in Flutter.
Rationale:
- 5-min chunks keep individual jobs < 3 min of GPU time โ fits comfortably in RunPod's 10-min execution budget
- 3-second overlap ensures that shots/bounces straddling a chunk boundary are visible in at least one chunk; the
mergeChunkResultsfunction deduplicates - Processing starts as soon as chunk 1 finalizes โ user sees progress immediately
- Per-chunk retry works naturally via Firebase Storage's resumable upload
Consequencesโ
Positiveโ
- These are benefits of the original proposal, not current product behavior.
- Server-side merge helpers remain reusable if physical chunking is revisited.
Negativeโ
- FFmpeg bloats the Flutter bundle by ~40 MB per platform
- Shot detection at chunk seams needs careful dedup logic (
deduplicateShotsinmergeChunkResults) sessionIdmust be passed through the whole pipeline so merging works- Client-side chunking is CPU-intensive on low-end devices (can take 30-60s for a 15-min video)
Neutralโ
- Introduces the concept of "session" (group of jobs) vs. "job" (single chunk) in the data model
- Adds one more Firestore query to reconstruct session state in the UI
Follow-up Actionsโ
- Integrate
ffmpeg_kit_fluttervia FFI or choose a lighter native trim/split implementation - Implement chunk upload queue with retry
- Write
mergeChunkResultscallable - Document overlap dedup in
acesense-auth-function/video/index.ts - Benchmark chunking time on low-end Android to decide if we need to split it
- Consider lazy FFmpeg loading โ only bundle when user starts their first upload
- Document the shipped contract as one upload โ one job/report
Referencesโ
mergeAnalyses+deduplicateShotsinacesense-auth-function/video/index.ts- Frontend Overview
- Data Model