Preparing Footage
This stage runs on a Mac or Linux machine with ffmpeg installed, not on the iPad. It converts one video into a set of clips plus a manifest.json describing them.
Contact frames are currently found by hand. The automatic detection pipeline is specified but not built — see Current Limitations.
1. Find the contact frames
Section titled “1. Find the contact frames”Play the video with frame numbers burned into the image:
ffplay -vf "drawtext=text='%{n}':fontsize=48:fontcolor=yellow:x=20:y=20" VIDEO.MOVStep through and note the frame number of each racket–shuttle contact.
To convert a timestamp you already know into a frame number:
python -m zhanyan_pipeline.handmake VIDEO.MOV --probe 34.52. Write the shot list
Section titled “2. Write the shot list”A CSV with one row per shot — contact_frame,zone[,shot_type]:
# badminton singles, club session 2026-08-104127,BL,clear5200,FR6890,FC,drop8144,BR,smashRules:
- Zone codes must match the sport. Badminton singles:
FL FC FR BL BC BR - Shot type is optional and unused in analysis — it is there for your own filtering later
- Frames must be positive, within the video, and each may appear only once
- Lines beginning with
#are ignored
3. Cut the clips
Section titled “3. Cut the clips”python -m zhanyan_pipeline.handmake VIDEO.MOV shots.csv \ --sport badminton_singles \ --out ./out/2026-08-10-club-a/ \ --viewpoint receiverThe output folder contains:
out/2026-08-10-club-a/├── manifest.json└── clips/ ├── s_0001.mp4 ├── s_0002.mp4 └── ...What the script guarantees
Section titled “What the script guarantees”Every clip ends exactly on its contact frame. This is the invariant the whole system rests on. Occlusion is achieved by stopping playback early, never by trimming a file — if clips were trimmed instead, every threshold would be displaced by the trimming error.
Clips are re-encoded, not stream-copied. A stream copy can only cut on a keyframe, which would move the end of the clip by up to a full GOP — and the end of the clip is the contact frame, the one thing that must be exact.
The script also rejects anything the app would reject on import: invalid zone codes, duplicate frames, frames past the end of the video.
4. Transfer to the iPad
Section titled “4. Transfer to the iPad”Copy the entire output folder into the iPad’s Files app via AirDrop, iCloud Drive, or a cable.