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

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):

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

ToolVersionNotes
Visual Studio 202217.xCommunity edition or higher, with the Desktop development with C++ workload. This supplies MSVC, a bundled CMake, and the Windows SDK.
CMake3.20 or newercmake_minimum_required(VERSION 3.20). Bundled with Visual Studio, or install standalone.
SDL33.xRequired for viewer and fileviewer. Everything else builds without it.
FFmpeg libav7.xDevelopment headers and import libraries and shared DLLs. See above.
Git2.xTo 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

  1. Download the SDL3 development libraries from github.com/libsdl-org/SDL/releases — take the SDL3-devel-<version>-VC.zip package.
  2. Extract to C:\SDL3. The directory must contain cmake/, include/ and lib/ subdirectories.
  3. Pass -DSDL3_DIR=C:/SDL3/cmake when configuring. If you installed elsewhere, point that at your own cmake/ 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)

  1. Download from github.com/BtbN/FFmpeg-Builds/releases.
  2. 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.
  3. Extract into external/ffmpeg/ at the repository root, flattened so that include/, lib/ and bin/ sit directly under VideoDistanceMarking\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.

  1. Install MSYS2 from msys2.org (default location C:\msys64).
  2. Launch the MSYS2 MinGW64 shell — not the plain MSYS2 MSYS shell. Confirm with echo $MSYSTEM, which must print MINGW64.
  3. 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
  4. Change into the repository and run the script. It defaults to FFmpeg 7.1:
    cd /c/path/to/VideoDistanceMarking
    export FFMPEG_VERSION=7.1
    bash tools/build_ffmpeg_from_source.sh
    The script downloads the tarball and signature from ffmpeg.org/releases and verifies the GPG signature. You may also pin FFMPEG_SHA256 if you want a checksum gate as well. The compile itself takes 15–40 minutes.
  5. 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.

#LocationHow to set it
1The CMake variable FFMPEG_ROOTcmake -B build -DFFMPEG_ROOT=D:\dev\ffmpeg-full-shared ...
2The environment variable FFMPEG_ROOTsetx FFMPEG_ROOT C:\ffmpeg-dev (then open a new shell)
3<repo>/external/ffmpeg/Extract the package there. This path is git-ignored.
4C:/ffmpeg-dev/Extract the package there.
5C:/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:

TargetRoleGated on
VDMController — the Win32 GUI and process manager; the only binary a human launches
captureUSB HDMI capture via Media Foundation, one instance per feed
motionFrame differencing, blob detection and movie triggering, one instance per feed
videogenFeed merge, H.264 encode, output managementFFmpeg
capfileContinuous mp4 recording of the live feedsFFmpeg
practice_playerPractice mode — plays saved video into the feed shared memoryFFmpeg
viewerLive grid-line viewer with distance readoutSDL3
fileviewerOffline viewer for saved mp4sSDL3
launch_viewerViewer launcher helper
NorgeReplaySeparate GUI replay product; not part of the capture pipeline
captestStandalone USB capture diagnostic
shmem_diagShared-memory health diagnostic
shmem_queryShared-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:

AreaTests
Infrastructuretest_shmem, test_settings, test_ipc, test_process_mgr, test_pipeline
Frame handlingtest_yuv_convert, test_yuv_reassemble, test_frame_diff
Motiontest_blob_detect, test_motion_trigger, test_motion_1feed, test_motion_2feed
Merge and outputtest_feed_merge, test_feed_merge_solo, test_videogen_dispatch, test_videogen_2feed
Practice playertest_practice_player_1feed, test_practice_player_2feed
Calibrationtest_auto_setup_1feed, test_auto_setup_2feed, test_gridlines
GPUtest_enumerate_gpus
Encoder FFmpeg onlytest_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:

FlagEffect
/O2Maximise speed
/OiEnable intrinsic functions
/OtFavour fast code
/W4High 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

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