Skip to content

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.

Play the video with frame numbers burned into the image:

Terminal window
ffplay -vf "drawtext=text='%{n}':fontsize=48:fontcolor=yellow:x=20:y=20" VIDEO.MOV

Step through and note the frame number of each racket–shuttle contact.

To convert a timestamp you already know into a frame number:

Terminal window
python -m zhanyan_pipeline.handmake VIDEO.MOV --probe 34.5

A CSV with one row per shot — contact_frame,zone[,shot_type]:

# badminton singles, club session 2026-08-10
4127,BL,clear
5200,FR
6890,FC,drop
8144,BR,smash

Rules:

  • 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
Terminal window
python -m zhanyan_pipeline.handmake VIDEO.MOV shots.csv \
--sport badminton_singles \
--out ./out/2026-08-10-club-a/ \
--viewpoint receiver

The output folder contains:

out/2026-08-10-club-a/
├── manifest.json
└── clips/
├── s_0001.mp4
├── s_0002.mp4
└── ...

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.

Copy the entire output folder into the iPad’s Files app via AirDrop, iCloud Drive, or a cable.