- C++ 66.2%
- C# 19.3%
- Kotlin 10.5%
- CMake 2.8%
- C 0.9%
- Other 0.3%
| android-client | ||
| docs | ||
| src | ||
| windows-ui/SecondMonitor.Desktop | ||
| .gitignore | ||
| build.bat | ||
| CMakeLists.txt | ||
| icon.ico | ||
| icon.png | ||
| README.md | ||
SecondMonitor
SecondMonitor is a native Windows display-streaming project with:
- a WinUI 3 desktop app for creating a virtual display, selecting a monitor, and starting the stream
- a native C++ bridge/core that handles monitor enumeration, virtual-display creation, H.264 encode/decode, and WebRTC transport
- an Android client that receives the WebRTC H.264 track and renders it with
MediaCodec
Repository layout
windows-ui/SecondMonitor.Desktop: WinUI 3 desktop appsrc: native Windows core and bridge sourcesandroid-client: Android tablet clientcmake: CMake helper modulesdocs: notes and planning docsthird_party: locally provisioned native dependencies, not committed
Windows prerequisites
Install these first:
- Visual Studio 2022 with MSVC C++ build tools
- CMake 3.21+
- Windows 10/11 SDK
- .NET 8 SDK with Windows desktop workload support
- OpenSSL for Windows
- FFmpeg shared build unpacked at
third_party/ffmpeg/pkg/ffmpeg-8.1-full_build-shared libdatachannelchecked out atthird_party/libdatachannelmbedtlschecked out atthird_party/mbedtls- an
MttVDDvirtual display driver payload, or another compatible indirect display driver package
Notes:
- The native Windows build links against OpenSSL through
libdatachannel. - FFmpeg is not discovered from the system; the current CMake expects the vendored path above.
- Creating a virtual monitor requires elevation.
- The virtual-display flow depends on the
MttVDDdriver and writes config under%ProgramData%\MttVDD.
Native dependencies
libdatachannel
git clone --recursive https://github.com/paullouisageneau/libdatachannel.git third_party/libdatachannel
mbedtls
git clone https://github.com/Mbed-TLS/mbedtls.git third_party/mbedtls
FFmpeg
Download a shared Windows build of FFmpeg 8.x and unpack it so these paths exist:
third_party/ffmpeg/pkg/ffmpeg-8.1-full_build-shared/includethird_party/ffmpeg/pkg/ffmpeg-8.1-full_build-shared/libthird_party/ffmpeg/pkg/ffmpeg-8.1-full_build-shared/bin
The current CMake helper expects DLL/import library names matching:
avcodec-62.dllavformat-62.dllavutil-60.dllswscale-9.dllswresample-6.dll
Virtual display driver
This repo does not include the actual virtual display driver package.
The current code looks for MttVDD.inf in:
C:\Windows\System32\DriverStore\FileRepository\mttvdd.inf_amd64_*\MttVDD.inf- a hardcoded developer fallback inside
src/VirtualDisplay.cpp
In practice:
- if the driver is already installed or staged in the Windows Driver Store, the app can usually find it automatically
- if not, you need to stage/install the driver yourself or update the fallback path in
src/VirtualDisplay.cpp
Build the desktop app
The desktop app depends on the native bridge DLL, so build in this order.
1. Build the native core and bridge
From the repo root:
build.bat
Or manually:
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
This produces:
build/bin/Release/SecondMonitor.exebuild/bin/Release/SecondMonitorBridge.dll
2. Build the WinUI desktop shell
From the repo root:
dotnet build windows-ui\SecondMonitor.Desktop\SecondMonitor.Desktop.csproj -c Release -p:Platform=x64
Desktop app output:
windows-ui/SecondMonitor.Desktop/bin/x64/Release/net8.0-windows10.0.19041.0/SecondMonitor.Desktop.exe
The desktop project copies SecondMonitorBridge.dll, FFmpeg DLLs, and Windows App SDK bootstrap/runtime DLLs into its output directory during build.
3. Run the desktop app
windows-ui\SecondMonitor.Desktop\bin\x64\Release\net8.0-windows10.0.19041.0\SecondMonitor.Desktop.exe
What the desktop app does:
- lists detected displays
- can create a second virtual display
- starts/stops the native streaming server
- can connect the built-in native client for local testing
- stores UI settings under
%LocalAppData%\SecondMonitor\desktop-settings.json
4. Build and install an MSIX locally
For local sideloading, the package must be signed with a certificate trusted by the machine that installs it.
Build a local MSIX:
dotnet publish windows-ui\SecondMonitor.Desktop\SecondMonitor.Desktop.csproj -c Release -p:Platform=x64 -p:GenerateAppxPackageOnBuild=true
Package output:
windows-ui/SecondMonitor.Desktop/bin/x64/Release/net8.0-windows10.0.19041.0/AppPackages/SecondMonitor.Desktop_1.0.0.0_x64_Test/SecondMonitor.Desktop_1.0.0.0_x64.msix
Important notes:
- an unsigned
.msixwill not install, even on the same development machine - the package identity publisher in
windows-ui/SecondMonitor.Desktop/Package.appxmanifestmust match the subject of the signing certificate - for self-signed local builds, trust the signing certificate in Windows before running the generated install script
- Visual Studio and the Windows App SDK packaging targets can generate
Add-AppDevPackage.ps1and a.cerfile next to the package; those are the easiest files to use for local sideload installs
Typical local install flow:
- Create or select a code-signing certificate whose subject matches the package publisher.
- Configure the desktop packaging profile to sign the package with that certificate.
- Build the package.
- Install the generated
.cerintoTrustedPeople. Some setups may also require trusting the certificate inRoot. - Run
Install.ps1orAdd-AppDevPackage.ps1from the generatedAppPackagesfolder.
Build the Android app
Android prerequisites:
- Android Studio or command-line SDK tools
- Android SDK Platform 34
- Android NDK 26.x
- Java 17
The Android native build also expects third_party/libdatachannel and third_party/mbedtls in the repo root.
From android-client:
gradlew.bat :app:assembleDebug
APK output:
android-client/app/build/outputs/apk/debug/app-debug.apk
Install to a connected device:
adb install -r android-client\app\build\outputs\apk\debug\app-debug.apk
Native CLI usage
The native executable still exists for direct testing and scripting:
List monitors:
build\bin\Release\SecondMonitor.exe --list
Create a virtual display and stream it:
build\bin\Release\SecondMonitor.exe --width 1920 --height 1080 --listen-port 5004
Capture an existing monitor instead:
build\bin\Release\SecondMonitor.exe --stream-monitor 1 --listen-port 5004
Run the Windows receive client:
build\bin\Release\SecondMonitor.exe --client --stream-host 127.0.0.1 --stream-port 5004
Current assumptions
- local build outputs, screenshots, and machine-specific config are not committed
third_partydependencies are provisioned locally- the top-level README is the main setup/build reference