Operating VDM
Running the system: launching the controller, what each tab does, the capture and recording workflow, how motion triggering behaves, measuring distances in the viewer, and calibrating a venue.
- Launching
- The controller window
- Reading the title bar
- Preview tab
- Processes tab
- Feeds and Settings tabs
- Menus
- Tournament-day workflow
- Motion detection
- Movie generation
- The viewer
- The fileviewer
- Practice mode
- Calibration
- Grid-line calibration
- Auto-setup
- Command-line reference
Launching
VDM.exe is the only binary you launch by hand. It presents the UI and spawns and
supervises everything else — capture, motion, videogen, capfile, practice_player, the
viewers. Start it from the Start Menu shortcut, from VDM.bat, or directly:
VDM.exe settings.txt
The settings file is a positional argument and is optional; without it VDM
searches for one. There is no
--settings flag.
The controller window
The controller is a tabbed window with four panels — Preview, Processes, Feeds and Settings — plus a Setup Grid Lines button sitting above the tab control that opens a modeless grid-line panel.
Reading the title bar
The title bar is the system's vital-signs display, and it is worth learning to read at a glance. It looks like this:
VDM - cap[1.2M, 1.2M, 1.2M] m[1.2M, 1.2M, 1.2M] out[42, 42, 42] Δ(0, 1, 0)
| Field | Meaning | What healthy looks like |
|---|---|---|
cap[…] | Capture sequence number per feed | All three climbing steadily and together |
m[…] | Motion sequence number per feed | Tracking close behind the capture numbers |
out[…] | Output-builder sequence per feed | Advancing when videogen is running |
Δ(…) | Spread across the feeds — how far apart they have drifted | Δ[0] under about 30. Δ[2] must always be 0. A Δ[0] that climbs without bound means a USB bandwidth problem — see Troubleshooting. |
If cap is advancing but m is not, motion detection is stalled or
switched off for that feed. If both advance but out does not, videogen is not
running or not receiving triggers.
Preview tab
The Preview tab shows the three live feeds plus a combined output preview, alongside the jumper list. A draggable splitter divides them.
The jumper list
Populated from jumpers.csv, with columns Active,
Number, Name, Club, Pract,
Rd 1 and Rd 2. A ► marks the active jumper
and the row is highlighted. Click a row to make that jumper active; by default the list
auto-advances after a capture. The active jumper determines whose number and name are overlaid
on the encoded clip, and which row a recorded distance is written to.
Preview tab buttons
| Button | What it does |
|---|---|
| Play / Pause | Freeze or resume the preview display. |
| Launch Practice | Start practice mode — spawns practice_player plus three motion processes and videogen. See Practice mode. |
| Feed 0 / Feed 1 / Feed 2 | Play a recorded file into that individual feed. |
| Force Video | Fire a synthetic motion trigger at the current playhead. The manual fallback when the auto-trigger misses a jumper, and the quickest end-to-end test of the encode path. |
| Sync Feeds | Re-synchronise the feeds. |
| Highlight ON / OFF | Motion-area flash overlay. Default on. Invaluable while tuning, distracting during an event. |
| Circles On / Off | Draw red circles around detected blobs directly into the feed frames. |
| Set Region 0 / 1 / 2 | Define the motion detection rectangle for a feed. Click once for the upper-left corner, once for the lower-right; keep redefining until you save. |
| Save Regions | Write the motion regions to settings.txt. |
| Ign Reg 0 / 1 / 2 | Define an ignore rectangle per feed, using the same two-click flow. |
| Save Motion Range | Write *saved_motion_range, the 12-integer backup of the regions. |
| Auto Setup / Abort | Run venue auto-setup. See Auto-setup. |
| Log On / Log Off | Enable the verbose debug logs. Off by default — turn it on before reproducing a problem. |
| motion shadow remove currently on / off | Toggle shadow removal in the motion detector. |
| Set F0 / F1 / F2 Offset | Enter per-feed vertical offset adjustment; the label flips to Exit FN Offset. The mouse wheel moves 1 pixel per notch, clamped to 0–540. |
| Capture Feeds | Start continuous per-feed recording; the label becomes Stop Capturing. |
| Pause Recording / Resume Recording | Pause and resume that recording without closing the files. |
| Setup Grid Lines | Open the grid-line calibration panel. See Grid-line calibration. |
Processes tab
Lists every spawned process with its name, PID, state (Running, Stopped, Failed), restart count and full command line. This is where you look first when something is not working.
| Button | What it does |
|---|---|
| Start All | Launch capture, motion and videogen for all three feeds. |
| Stop All | Stop processes — but see the note below. |
| Start Capture 0 / 1 / 2 | Start an individual capture process. |
| Start Motion 0 / 1 / 2 | Start an individual motion process. |
| Start VideoGen | Start videogen. |
| Start Viewer | Open the live viewer window. |
Stop All behaves differently in practice mode
If practice_player.exe is running, Stop All exits practice
mode and deliberately leaves the motion processes running. Click it a second time to force a
full stop. Outside practice mode a single click stops everything.
A rising restart count for a process is significant: the controller auto-restarts children that exit, so a capture process cycling Running ↔ Stopped shows up here as a climbing number rather than as an obvious failure. That pattern usually means antivirus interference — see Troubleshooting.
Feeds and Settings tabs
The Feeds tab toggles motion detection on and off per feed. These toggles
affect the running system only — they are not written to settings.txt, so the
feed_motion_on[N] values are restored at the next launch.
The Settings tab is a plain text editor over settings.txt with
Load Settings and Save Settings buttons. Remember that saving
rewrites the entire file — see
Configuration § how the file is written back.
Menus
| Menu item | What it does |
|---|---|
| Tools → Select Cameras… | Assign Media Foundation devices to feeds. One row per feed, labelled Feed 0 (TOP), Feed 1 (MIDDLE), Feed 2 (BOTTOM), each with a combo box plus None. Buttons [Swap 0↔1], [Swap 0↔2], [Swap 1↔2] reorder without re-plugging cables. Assigning a device auto-starts capture and motion for that feed; None stops both. |
| Tools → Select GPU | Choose the NVENC GPU, writing *capfile_nvenc_gpu_name. Required on every new machine. |
| Tools → Select Practice Files… | Choose the three video files for practice mode. Three rows each with Browse… and Clear. Writes *practice_video_path[0..2]. |
| Capture → Start File Capture… | Open the file-capture panel for continuous per-feed recording. |
| Help (or F1) | Open the documentation. |
Tournament-day workflow
During a competition you generally want both things running: the auto-triggered per-jumper clips, and a continuous recording of each feed for later review.
Auto-triggered jumper clips
These need no operator action. Whenever motion detects a jumper passing through the middle frame, videogen encodes a composite clip and writes it to the archive directory. Your only involvement is keeping the correct jumper active in the list, and pressing Force Video if a jump is missed.
Continuous recording (capfile)
- Preview tab → Capture Feeds.
- The first time in a session you are prompted for an output directory. A per-event
subfolder works well. The choice is saved to
*capfile_output_dir; leaving it blank usesCapturedVideosunder the working directory. - Recording starts. Files are named
cap_F_YYYY-MM-DD-HH.MM.SS.mp4, where F is the feed index and the timestamp is when the panel was opened — so all three feeds share one timestamp and sort together. Files roll over at 2 GB, adding_part2,_part3and so on. At the default quality setting expect roughly 3 hours per 2 GB per feed. - During dead time — between rounds, a bus run, a weather hold — click Pause Recording. The files stay open and the pause-gap wall time is dropped from the output timeline, so the result is a single seamless mp4 rather than a file with a jump in it.
- Resume Recording between jumps.
- At the end, stop the recording from the button. This finalises the moov atom so the mp4 plays in any standard player. Exit waits up to 8 seconds per process for that to complete.
Never kill a recording with Task Manager
Killing capfile.exe mid-recording — via Task Manager, Ctrl-C, or by
powering off — leaves the mp4 without a moov atom, and it will not play in anything.
There is no reliable way to recover the file afterwards. Always use the stop button.
Motion detection
Each motion.exe instance watches one feed's shared-memory ring and, for every
frame: computes pixel differences against the previous frame, builds histograms and derives
adaptive thresholds, produces a binary motion map, finds connected-component blobs, tracks them
across frames, and decides whether to start or end a movie.
Blobs
A blob is a connected component found by 8-neighbour flood fill, carrying a
centroid and a bounding box. Nearby blobs are merged within the fuzz tolerance
(blob_fuzz_x, blob_fuzz_y) and then filtered by size against
min_blob_sizes[] and max_blob_size. Up to 2000 are tracked internally;
they are sorted by pixel count descending and the top 10 are written into the frame header.
Triggering
A movie starts when the motion count reaches min_motion_count[] consecutive
frames, and ends after min_nomotion_count[] consecutive frames without motion.
min_frames_between_movies debounces successive triggers. The middle camera, feed 1,
is the trigger camera.
Type 1 and type 2 movies
The composite output puts feed 1 in the top half of the frame. What goes in the bottom half depends on where the jumper is:
- Type 0 / type 1 — feed 1 on top, feed 0 (the approach and takeoff) below.
- Type 2 — feed 1 on top, feed 2 (the landing and outrun) below. Motion switches the output to type 2 at the scene trigger, so the clip follows the jumper down the hill.
min_type2_frames sets the minimum length for a type-2 movie.
Tuning
| Problem | Adjustment |
|---|---|
| False triggers — movies committed with no jumper | Narrow the motion region to exclude moving trees and flags; raise min_motion_count[]; raise min_blob_sizes[]; place an ignore rectangle over the offender with Ign Reg N. |
| Missed jumpers | Lower min_motion_count[] for a faster trigger; lower min_blob_sizes[] to catch smaller or more distant figures; widen the motion region. |
| Clips cut short before the landing | Raise min_nomotion_count[] for that feed; raise movie_pad or the pre/post padding keys. |
| No motion detected at all | Check feed_motion_on[N]=1; confirm the region actually covers the landing hill; turn on Log On and read motion_debug_N.log. |
Turn on Highlight and Circles while tuning — seeing
which blobs are detected and which are discarded is far faster than reasoning about threshold
numbers. motion_debug_1.log is usually the most informative log, since feed 1 is the
trigger camera.
Movie generation
videogen.exe polls for movie boundaries roughly every 200 ms. When one appears it
overlays the active jumper's number and name onto each frame, publishes the clip metadata, and
encodes.
| Aspect | Detail |
|---|---|
| Output name | jump_YYYYMMDD_HHMMSS_NNN.mp4 |
| Output directory | *archive_dir, default output_videos. Created at startup if missing. |
| Hardware encode | h264_nvenc with -preset p4 -rc vbr -cq 20 -b:v 0 |
| Software fallback | libx264 with -preset fast -crf 18 |
| Overlay font | numbers.txt, the bitmap glyph table, which must sit beside the executables |
Upload
After each encode, videogen spawns upload_video.bat with the output file path.
The script FTPs the file using curl.exe and then fetches a signal URL. It retries
three times with a 10-second pause between attempts. If all three fail, the mp4 stays in the
archive directory and a failure line is appended to upload_video.log; you can re-run
the upload by hand once the network is fixed:
.\upload_video.bat output_videos\jump_20260612_141530_007.mp4
Set upload_enabled=0 to skip uploading entirely — useful for venue testing
on a closed network. Credentials come from %USERPROFILE%\.netrc, never from
settings.txt; see Installation § upload
credentials.
The viewer
viewer.exe is where distances are actually measured. It reads committed frames
from shared memory and draws the calibrated grid lines over them. Launch it with
Start Viewer on the Processes tab, or directly as
viewer.exe [settings_file].
Move the mouse and the readout gives the distance at the cursor position, interpolated between adjacent grid lines and rounded to the nearest half metre.
Keyboard
| Key | Action |
|---|---|
| → / ← | Step forward / back one frame |
| ↓ / ↑ | Jump 7 frames (15 with Shift) |
| Page Down / Page Up | Jump 25 frames (50 with Shift) |
| Ctrl+Page Down / Page Up | Next / previous channel in the review ring |
| Ctrl (held) | Show the CH N channel overlay |
| 1 | Toggle the main grid lines |
| 2 | Toggle the sub-metre grid lines |
| G | Cycle the grid feed override |
| F11 | Fullscreen |
| F1 | Help |
| Esc or Q | Quit |
Mouse
| Action | Effect |
|---|---|
| Move | Distance readout updates at the cursor position |
| Shift + left-click | Record the distance for the active jumper. A plain left-click does nothing — this is deliberate, so a stray click cannot overwrite a result. |
| Right-button drag | Fast scrub through frames. Speed is set by mouse_position_speed; hold Shift for double speed. |
| Scroll wheel | Step one frame |
The review ring holds viewer_ring_channels recent movies, 4 by default, so you
can page back to the previous jumper without leaving the viewer. Window state is remembered in
viewer.ini.
The fileviewer
fileviewer.exe reviews saved clips offline, using the same renderer and the same
grid-line projection as the live viewer.
fileviewer.exe [video_file_or_url] [settings_file]
It accepts a local file path, an http(s) URL (in which case it also fetches the companion
settings.txt, so the grid lines match the clip), or no argument at all, in which
case it opens a file dialog. It loads up to 2000 frames — roughly 4.6 GB — and
truncates anything longer.
Controls match the viewer, minus the Shift-modified page jumps and without the G and F11 bindings.
Practice mode
Practice mode replays recorded video through the real pipeline: motion detection, triggering, merging and encoding all run exactly as they would live. It is the way to rehearse, to tune thresholds against known footage, and to test an install without cameras.
- Choose the three files under Tools → Select Practice Files….
These are stored in
*practice_video_path[0..2]. Left blank, practice_player falls back to a legacy file-naming convention. - Preview tab → Launch Practice. Four processes spawn:
practice_player.exeplus threemotion.exe. - Let it run. Movies commit automatically as jumpers are detected.
- Stop All to exit — remembering that in practice mode the first click leaves the motion processes running, so click twice for a full stop. Check Task Manager for orphans afterwards.
The sample footage package is designed for exactly this.
Calibration
Calibration happens once, when the cameras are first mounted, and again after any physical movement. There are three parts, in this order.
1. Motion regions
- Preview tab. For each feed, click Set Region N.
- Click the upper-left corner of the area where motion should be detected, then the lower-right. Aim at the landing zone and exclude the spectator stands. The rectangle draws in red on the preview.
- Keep redefining until it looks right — nothing is committed until you save.
- Click Save Regions.
If something inside the region moves constantly — a flag, a marshal's post — add an ignore rectangle over it with Ign Reg N rather than shrinking the whole region.
2. Grid lines
See below. Budget about 30 minutes on a first deployment.
3. Auto-setup
Optional but recommended. See below.
Grid-line calibration
Grid lines are the whole basis of distance measurement. Each metre line is defined by two anchor points per feed: a far point where that metre mark crosses the far edge of the hill, up toward the vanishing point, and a near point at the near edge, toward the bottom of the frame. The viewer draws the line between them and interpolates distances between adjacent lines.
You calibrate against a recorded movie, not the live preview
The grid-line workflow needs a committed movie displayed in the viewer, and at least one process running. Capture a clip first — Force Video is enough — then calibrate against it.
- Click Setup Grid Lines above the tab control. The panel opens.
- Pick the feed, the anchor type and the metre mark — for example Feed 1 » Near » 40.
- The viewer displays a banner:
CLICK NEAR POINT TWICE - FEED 1 - 40m. - Click twice on the point. The first click only moves window focus; the second is the one that registers. This trips up everyone once.
- The panel's button label changes to
*40, marking that anchor as set. The value is written tosettings.txtimmediately and the line redraws on the next frame. - Repeat for every anchor on every feed, then Save or Exit to close the panel.
Clicking in the wrong half of the frame raises a warning dialog; dismiss it and press the button again. The frame's top half is always feed 1. The bottom half is feed 0 on type 1 frames and feed 2 on type 2 frames, so which camera you are calibrating depends on the type of the movie currently displayed.
The panel's position is remembered in gridline_panel.ini, stored next to
settings.txt. Its width and height are recomputed from the metre-line count each
time it opens.
Checking the result
- Press 1 in the viewer to show the main grid lines.
- Move the mouse to a known hill marking and confirm the readout matches.
- Lines should converge toward a vanishing point at the top of the hill, and near-anchor spacing should be visibly wider than far-anchor spacing. If it is not, an anchor is misplaced.
- Confirm each feed has as many populated anchor pairs as its
meter_line_countclaims — a line with unset anchors has nothing to draw between.
Anchors can also be edited directly as @far[F][I] and @near[F][I]
keys — see Configuration § grid lines —
but the panel is far quicker and less error-prone.
Auto-setup
Auto-setup derives motion timing and pixel-to-metre interpolation from observed practice runs,
producing pixel_frames.txt.
Prerequisites: grid lines already calibrated on all three feeds, motion regions already set, and either practice footage or live cameras with jumpers to watch.
Click Auto Setup in the Preview tab; the label changes to Abort. The process then:
- Backs up the current motion regions.
- Turns on logging and blob circles.
- Launches the practice player.
- Runs until it hits
auto_setup_max_jumperscompleted jumpers orauto_setup_max_framesfeed-1 frames, whichever comes first — or until the player stops, or three seconds pass with no frame advance. - Parses
motion_debug_0.log,motion_debug_1.logandmotion_debug_2.log, computes the timings and regions, and restores the backed-up X ranges. - Writes
settings.txtandpixel_frames.txt, then shuts the application down so the new settings take effect on the next launch.
Auto-setup rewrites settings.txt
It overwrites your configuration on completion. Take a copy of a known-good
settings.txt somewhere VDM does not write before running it. Clicking
Abort or closing the window writes nothing, so an aborted run is safe.
The shipped configuration sets auto_setup_max_jumpers=1, which stops after a
single jumper. The workflow is designed around a larger sample — on the order of six to
ten runs — so raise that value before a real calibration session.
verify on target hardware
You can also run it headlessly against saved footage, without touching the UI:
VDM.exe --auto-setup --max-movies 8
Command-line reference
VDM.exe
VDM.exe [settings_file]
VDM.exe --help
VDM.exe --auto-setup [--max-movies N] [--max-frames M]
VDM.exe --practice-test [--max-movies N] [--max-frames M]
VDM.exe --capture-test [--source practice|usb] [--duration SECS] [--pause-before-start SECS]
| Flag | Meaning |
|---|---|
--help, -h | Print usage and exit. |
--auto-setup | Run venue auto-setup headlessly. |
--practice-test | Run practice mode as an automated test. |
--capture-test | Drive the file-capture panel headlessly: open the panel, optionally launch the practice player, wait, start recording, record for the duration, then exit and wait for a clean mp4 finalise. |
--max-movies N | Stop after N movies. |
--max-frames M | Stop after M frames. |
--source practice | usb | Capture-test source. practice launches the practice player; usb assumes live capture.exe feeds. |
--duration SECS | Capture-test recording length. Default 30 seconds. |
--pause-before-start SECS | Capture-test delay before pressing start. Default 0. |
--practice-dir <path> | Directory to take practice files from. |
--auto-setup, --practice-test and --capture-test are
mutually exclusive. Any unrecognised token beginning with - is a parse error, and any
token not beginning with - is taken as the settings-file path — a second one is
an error.
captest.exe
A standalone USB capture diagnostic. It does not touch shared memory or read
settings.txt, which is what makes it useful: if captest sees a camera and VDM does
not, the problem is in VDM's configuration, not in Windows or the hardware.
captest.exe --list
captest.exe --device-path <symbolic-link>
--list enumerates the Media Foundation capture devices. Opening a device shows a
resizable 1280×720 preview window titled
captest — <name> — frame N — XX.X fps. Esc
closes it.
Diagnostics
| Tool | Purpose |
|---|---|
shmem_diag.exe | Report shared-memory region health. Run it with nothing else running to detect stale regions left by a crashed process. |
shmem_query.exe | Query current shared-memory state. |
Log files
All processes run with CREATE_NO_WINDOW, so there is no console output —
everything goes to log files in the install directory: controller_debug.log,
capture_debug_N.log, motion_debug_N.log,
videogen_debug.log, ffmpeg_debug.log, viewer_debug.log,
capfile_debug.log, practice_player_debug.log and
captest_debug.log.
The verbose ones only fill up when logging is enabled, so turn on Log On
before reproducing a problem. When reporting a bug, zip every *_debug*.log plus
settings.txt and pixel_frames.txt — that bundle is enough to
reproduce most issues offline.
