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

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)
FieldMeaningWhat healthy looks like
cap[…]Capture sequence number per feedAll three climbing steadily and together
m[…]Motion sequence number per feedTracking close behind the capture numbers
out[…]Output-builder sequence per feedAdvancing 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

ButtonWhat it does
Play / PauseFreeze or resume the preview display.
Launch PracticeStart practice mode — spawns practice_player plus three motion processes and videogen. See Practice mode.
Feed 0 / Feed 1 / Feed 2Play a recorded file into that individual feed.
Force VideoFire 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 FeedsRe-synchronise the feeds.
Highlight ON / OFFMotion-area flash overlay. Default on. Invaluable while tuning, distracting during an event.
Circles On / OffDraw red circles around detected blobs directly into the feed frames.
Set Region 0 / 1 / 2Define 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 RegionsWrite the motion regions to settings.txt.
Ign Reg 0 / 1 / 2Define an ignore rectangle per feed, using the same two-click flow.
Save Motion RangeWrite *saved_motion_range, the 12-integer backup of the regions.
Auto Setup / AbortRun venue auto-setup. See Auto-setup.
Log On / Log OffEnable the verbose debug logs. Off by default — turn it on before reproducing a problem.
motion shadow remove currently on / offToggle shadow removal in the motion detector.
Set F0 / F1 / F2 OffsetEnter 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 FeedsStart continuous per-feed recording; the label becomes Stop Capturing.
Pause Recording / Resume RecordingPause and resume that recording without closing the files.
Setup Grid LinesOpen 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.

ButtonWhat it does
Start AllLaunch capture, motion and videogen for all three feeds.
Stop AllStop processes — but see the note below.
Start Capture 0 / 1 / 2Start an individual capture process.
Start Motion 0 / 1 / 2Start an individual motion process.
Start VideoGenStart videogen.
Start ViewerOpen 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.

Menu itemWhat 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)

  1. Preview tab → Capture Feeds.
  2. 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 uses CapturedVideos under the working directory.
  3. 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, _part3 and so on. At the default quality setting expect roughly 3 hours per 2 GB per feed.
  4. 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.
  5. Resume Recording between jumps.
  6. 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:

min_type2_frames sets the minimum length for a type-2 movie.

Tuning

ProblemAdjustment
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.

AspectDetail
Output namejump_YYYYMMDD_HHMMSS_NNN.mp4
Output directory*archive_dir, default output_videos. Created at startup if missing.
Hardware encodeh264_nvenc with -preset p4 -rc vbr -cq 20 -b:v 0
Software fallbacklibx264 with -preset fast -crf 18
Overlay fontnumbers.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

KeyAction
/ Step forward / back one frame
/ Jump 7 frames (15 with Shift)
Page Down / Page UpJump 25 frames (50 with Shift)
Ctrl+Page Down / Page UpNext / previous channel in the review ring
Ctrl (held)Show the CH N channel overlay
1Toggle the main grid lines
2Toggle the sub-metre grid lines
GCycle the grid feed override
F11Fullscreen
F1Help
Esc or QQuit

Mouse

ActionEffect
MoveDistance readout updates at the cursor position
Shift + left-clickRecord 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 dragFast scrub through frames. Speed is set by mouse_position_speed; hold Shift for double speed.
Scroll wheelStep 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.

  1. 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.
  2. Preview tab → Launch Practice. Four processes spawn: practice_player.exe plus three motion.exe.
  3. Let it run. Movies commit automatically as jumpers are detected.
  4. 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

  1. Preview tab. For each feed, click Set Region N.
  2. 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.
  3. Keep redefining until it looks right — nothing is committed until you save.
  4. 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.

  1. Click Setup Grid Lines above the tab control. The panel opens.
  2. Pick the feed, the anchor type and the metre mark — for example Feed 1 » Near » 40.
  3. The viewer displays a banner: CLICK NEAR POINT TWICE - FEED 1 - 40m.
  4. Click twice on the point. The first click only moves window focus; the second is the one that registers. This trips up everyone once.
  5. The panel's button label changes to *40, marking that anchor as set. The value is written to settings.txt immediately and the line redraws on the next frame.
  6. 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

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:

  1. Backs up the current motion regions.
  2. Turns on logging and blob circles.
  3. Launches the practice player.
  4. Runs until it hits auto_setup_max_jumpers completed jumpers or auto_setup_max_frames feed-1 frames, whichever comes first — or until the player stops, or three seconds pass with no frame advance.
  5. Parses motion_debug_0.log, motion_debug_1.log and motion_debug_2.log, computes the timings and regions, and restores the backed-up X ranges.
  6. Writes settings.txt and pixel_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]
FlagMeaning
--help, -hPrint usage and exit.
--auto-setupRun venue auto-setup headlessly.
--practice-testRun practice mode as an automated test.
--capture-testDrive 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 NStop after N movies.
--max-frames MStop after M frames.
--source practice | usbCapture-test source. practice launches the practice player; usb assumes live capture.exe feeds.
--duration SECSCapture-test recording length. Default 30 seconds.
--pause-before-start SECSCapture-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

ToolPurpose
shmem_diag.exeReport shared-memory region health. Run it with nothing else running to detect stale regions left by a crashed process.
shmem_query.exeQuery 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.