Building from source
For developers. If you were handed a compiled package and just want VDM running on a laptop, you do not need any of this — go to Installation instead.
- Read this first: FFmpeg is required to build
- Toolchain
- Installing SDL3
- Installing FFmpeg libav
- How CMake finds FFmpeg
- Configure and build
- Build outputs
- Running the tests
- Compiler and linker flags
- Build troubleshooting
Read this first: FFmpeg is required to build
Correction to BUILD.md
BUILD.md in the repository root states that FFmpeg is not required for
building
. That is false. Do not rely on it.
CMakeLists.txt gates four targets behind if(FFMPEG_LIBAV_FOUND):
capfile— mp4 recording of the live feedsvideogen— feed merge and H.264 encoding, the core of the pipelinepractice_player— practice mode from saved videotest_libav_encoder_stitching— the encoder stitching test
Without FFmpeg development headers and import libraries on the machine, CMake
configures happily, the build succeeds, and those three executables simply are not there. There
is no error — you get a build that looks fine and a pipeline that cannot encode anything.
Confirm the FFmpeg detection message during configure, and confirm
videogen.exe exists in build\Release\ afterwards.
Shared FFmpeg DLLs alone are not enough for a build. You need the include/ and
lib/ trees as well — that is, a development or shared
distribution, not the -essentials package that ships only
ffmpeg.exe.
Toolchain
| Tool | Version | Notes |
|---|---|---|
| Visual Studio 2022 | 17.x | Community edition or higher, with the Desktop development with C++ workload. This supplies MSVC, a bundled CMake, and the Windows SDK. |
| CMake | 3.20 or newer | cmake_minimum_required(VERSION 3.20). Bundled with Visual Studio, or install standalone. |
| SDL3 | 3.x | Required for viewer and fileviewer. Everything else builds without it. |
| FFmpeg libav | 7.x | Development headers and import libraries and shared DLLs. See above. |
| Git | 2.x | To clone the repository. |
The codebase is native C11 targeting Windows. There is no C++ in the product code, no cross-platform layer, and no scripting runtime at build time.
Installing SDL3
- Download the SDL3 development libraries from
github.com/libsdl-org/SDL/releases
— take the
SDL3-devel-<version>-VC.zippackage. - Extract to
C:\SDL3. The directory must containcmake/,include/andlib/subdirectories. - Pass
-DSDL3_DIR=C:/SDL3/cmakewhen configuring. If you installed elsewhere, point that at your owncmake/directory.
To build SDL3 from source instead:
git clone https://github.com/libsdl-org/SDL.git
cd SDL
cmake -B build -G "Visual Studio 17 2022" -DCMAKE_INSTALL_PREFIX=C:/SDL3
cmake --build build --config Release
cmake --install build --config Release
Building without SDL3
If SDL3 is not found, only viewer and fileviewer are skipped;
everything else builds normally. CMake prints
SDL3 not found via find_package. Set SDL3_DIR or install SDL3. This is a genuine
warning you will see — unlike the FFmpeg case, which is silent.
Installing FFmpeg libav
You need a shared build with development files. The -shared
suffix in the package name is the thing to look for; a static -essentials build
contains only ffmpeg.exe and will not satisfy the build.
Option A — prebuilt from BtbN (recommended in the project's own dev docs)
- Download from github.com/BtbN/FFmpeg-Builds/releases.
- Take
ffmpeg-master-latest-win64-lgpl-shared.zip. The lgpl flavour is preferred if the result will be redistributed; the GPL flavours pull in x264/x265. - Extract into
external/ffmpeg/at the repository root, flattened so thatinclude/,lib/andbin/sit directly underVideoDistanceMarking\external\ffmpeg\.
DLL version suffixes will not match the shipped set
BtbN's master builds currently produce avcodec-62 /
avfilter-11-generation DLLs, whereas the runtime set shipped with VDM is the
FFmpeg 7.1 generation: avcodec-61, avformat-61,
avutil-59, avfilter-10, swscale-8,
swresample-5 (plus avdevice-61). If you switch FFmpeg generations,
purge the old DLLs from the output directory — a mixed set produces
load failures that look like missing-export errors rather than version mismatches.
Option B — prebuilt from gyan.dev
From gyan.dev/ffmpeg/builds, take
ffmpeg-release-full-shared.7z (or a version-tagged equivalent). Requires 7-Zip to
unpack. Note that gyan.dev's full builds are GPL-licensed because of x264/x265; if you
redistribute, ship FFmpeg's LICENSE.txt alongside.
Option C — vcpkg
vcpkg install ffmpeg[core,avcodec,avformat,avfilter,avutil,swscale]:x64-windows
Option D — build FFmpeg from source under MSYS2
The repository carries tools/build_ffmpeg_from_source.sh for this. Budget
35–60 minutes for a first run.
- Install MSYS2 from msys2.org (default location
C:\msys64). - Launch the MSYS2 MinGW64 shell — not the plain
MSYS2 MSYS
shell. Confirm withecho $MSYSTEM, which must printMINGW64. - Update and install the toolchain:
pacman -Syu pacman -S --needed base-devel mingw-w64-x86_64-toolchain \ mingw-w64-x86_64-nasm mingw-w64-x86_64-yasm \ mingw-w64-x86_64-pkg-config mingw-w64-x86_64-gcc git curl gnupg - Change into the repository and run the script. It defaults to FFmpeg 7.1:
The script downloads the tarball and signature fromcd /c/path/to/VideoDistanceMarking export FFMPEG_VERSION=7.1 bash tools/build_ffmpeg_from_source.shffmpeg.org/releasesand verifies the GPG signature. You may also pinFFMPEG_SHA256if you want a checksum gate as well. The compile itself takes 15–40 minutes. - Return to a normal Windows shell for the CMake build — do not build VDM from inside the MSYS2 shell.
The configure line the script uses is deliberately narrow: --disable-everything
with selective re-enabling, plus --disable-programs, --disable-doc,
--disable-network, --disable-static, --disable-debug and
--enable-optimizations. The result is a small shared build carrying only what VDM
actually links against.
How CMake finds FFmpeg
cmake/FindFFmpegLibav.cmake probes these locations in this order
and stops at the first hit. Each probe looks for
<root>/include/libavcodec/avcodec.h.
| # | Location | How to set it |
|---|---|---|
| 1 | The CMake variable FFMPEG_ROOT | cmake -B build -DFFMPEG_ROOT=D:\dev\ffmpeg-full-shared ... |
| 2 | The environment variable FFMPEG_ROOT | setx FFMPEG_ROOT C:\ffmpeg-dev (then open a new shell) |
| 3 | <repo>/external/ffmpeg/ | Extract the package there. This path is git-ignored. |
| 4 | C:/ffmpeg-dev/ | Extract the package there. |
| 5 | C:/ffmpeg/ | Extract the package there. |
On success the module sets the cache variable FFMPEG_LIBAV_FOUND to
TRUE, derives FFMPEG_INCLUDE_DIR, FFMPEG_LIB_DIR and
FFMPEG_BIN_DIR from the detected root, and defines the imported interface target
FFmpeg::Libav. It resolves four components by import library:
avformat, avcodec, avutil and swscale
(as avcodec.lib and friends; MinGW-style libavcodec.dll.a names are
accepted as a fallback).
A helper function vdm_install_ffmpeg_dlls(target) copies the runtime DLLs
alongside the built executables. The DLL glob is wider than the four link components — it
also collects swresample-*, avfilter-*, avdevice-*,
postproc-*, libbz2-*, libwinpthread-*,
libiconv-* and zlib*.
Configure and build
Open a Developer Command Prompt for VS 2022 or a Developer PowerShell for VS 2022, then:
cd VideoDistanceMarking
cmake -B build -G "Visual Studio 17 2022" -DSDL3_DIR=C:/SDL3/cmake
cmake --build build --config Release
Add -DFFMPEG_ROOT=... if your FFmpeg is somewhere the
search order does not cover.
If CMake is not on PATH, use the copy bundled with Visual Studio:
"C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -B build -G "Visual Studio 17 2022" -DSDL3_DIR=C:/SDL3/cmake
Check the configure output before you walk away
Scan the configure log for the FFmpeg detection result and the SDL3 result. Those two lines determine whether you are about to build the whole product or a subset of it. Everything else in a normal configure run is noise.
Build outputs
All executables land in build/Release/. Thirteen non-test executables are
defined:
| Target | Role | Gated on |
|---|---|---|
VDM | Controller — the Win32 GUI and process manager; the only binary a human launches | — |
capture | USB HDMI capture via Media Foundation, one instance per feed | — |
motion | Frame differencing, blob detection and movie triggering, one instance per feed | — |
videogen | Feed merge, H.264 encode, output management | FFmpeg |
capfile | Continuous mp4 recording of the live feeds | FFmpeg |
practice_player | Practice mode — plays saved video into the feed shared memory | FFmpeg |
viewer | Live grid-line viewer with distance readout | SDL3 |
fileviewer | Offline viewer for saved mp4s | SDL3 |
launch_viewer | Viewer launcher helper | — |
NorgeReplay | Separate GUI replay product; not part of the capture pipeline | — |
captest | Standalone USB capture diagnostic | — |
shmem_diag | Shared-memory health diagnostic | — |
shmem_query | Shared-memory state query | — |
There is also a static library target common (shared types, shared memory, IPC,
settings parsing, logging) and a custom target copy_help_docs.
There is no fileplayer target
src/fileplayer/ exists as a source directory and CMakeLists.txt
carries a commented-out add_executable(fileplayer ...) block, but no
fileplayer binary is built. Older documentation — including
USERGUIDE.md — refers to fileplayer.exe for playing recorded
video into a feed. The target that actually ships is practice_player.
Running the tests
Twenty-three tests are registered with CTest — twenty-two unconditional, plus
test_libav_encoder_stitching which only exists when FFmpeg was found.
ctest --test-dir build -C Release
Individual tests can be run directly from build\Release\. The registered set:
| Area | Tests |
|---|---|
| Infrastructure | test_shmem, test_settings, test_ipc, test_process_mgr, test_pipeline |
| Frame handling | test_yuv_convert, test_yuv_reassemble, test_frame_diff |
| Motion | test_blob_detect, test_motion_trigger, test_motion_1feed, test_motion_2feed |
| Merge and output | test_feed_merge, test_feed_merge_solo, test_videogen_dispatch, test_videogen_2feed |
| Practice player | test_practice_player_1feed, test_practice_player_2feed |
| Calibration | test_auto_setup_1feed, test_auto_setup_2feed, test_gridlines |
| GPU | test_enumerate_gpus |
| Encoder FFmpeg only | test_libav_encoder_stitching |
If your test count comes back as 22 rather than 23, FFmpeg was not detected — go back to the top of this page. That count is the cheapest available check on whether the encode path built.
Compiler and linker flags
Under MSVC the build applies:
| Flag | Effect |
|---|---|
/O2 | Maximise speed |
/Oi | Enable intrinsic functions |
/Ot | Favour fast code |
/W4 | High warning level |
/WX- | Warnings are not errors — deliberate, to tolerate C4324 alignment-padding warnings |
Also defined project-wide: _CRT_SECURE_NO_WARNINGS and
WIN32_LEAN_AND_MEAN. On non-MSVC compilers the equivalent set is
-O2 -Wall -Wextra.
Three targets link with /STACK:4194304 — a 4 MB stack:
motion, test_blob_detect and test_pipeline. The blob
detector is recursive and overflows the default 1 MB stack on large connected components. If you
add a new target that calls into blob detection, it needs the same link option.
Build troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Build succeeds but videogen.exe, capfile.exe or practice_player.exe is missing from build\Release\ |
FFmpeg development files were not found. FFMPEG_LIBAV_FOUND is false and the targets were never defined. |
Install a shared/dev FFmpeg build and re-run configure. Verify with the search order, or force it with -DFFMPEG_ROOT=.... Delete build/ and reconfigure if the cache has a stale FFMPEG_LIBAV_FOUND=FALSE. |
SDL3 not found via find_package. Set SDL3_DIR or install SDL3. |
SDL3 is absent or in an unexpected location. | Pass -DSDL3_DIR=C:/SDL3/cmake, adjusted to your install path. |
LNK1104: cannot open file '....exe' |
Antivirus has the output executable locked or has just deleted it. Norton in particular quarantines freshly built unsigned binaries. | Add build/Release/ to the antivirus exclusion list, then rebuild. See the antivirus section — the same failure mode bites at deployment time. |
Stack overflow inside motion.exe |
Recursive blob detection exceeding the default stack. | Should not happen on a stock build — /STACK:4194304 is applied automatically. If you have added a target that calls blob detection, give it the same link option. |
Missing-export or ordinal errors loading av* DLLs at runtime |
Mixed FFmpeg generations in the output directory — for example avcodec-61 alongside a leftover avutil-58. |
Delete every av*.dll, sw*.dll and postproc*.dll from the output directory and let the build recopy a single consistent set. |
| CMake configure fails immediately on version | CMake older than 3.20. | Use the CMake bundled with Visual Studio 2022, or install 3.20+ standalone. |
Deploying a build
To turn build\Release\ into a runnable install, copy its contents to
C:\VDM\, then add the data files that live in the repository root rather than the
build tree — numbers.txt, jumpers.csv, NorgeLogo.png
and a settings.txt. Those must sit next to the executables. The full deployment
shape is documented in Installation § folder layout.
