diff --git a/.github/workflows/ci-bundle.yml b/.github/workflows/ci-bundle.yml
index 60a57c60237..a6642de6454 100644
--- a/.github/workflows/ci-bundle.yml
+++ b/.github/workflows/ci-bundle.yml
@@ -23,6 +23,10 @@ jobs:
- name: Install npm dependencies
run: npm install --ignore-scripts
+ - name: Debug install
+ if: always()
+ run: cat "${HOME}/.npm/_logs/*-debug-0.log" || true
+
- name: Build
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml
index 1a40066db37..878c27013e9 100644
--- a/.github/workflows/ci-windows.yml
+++ b/.github/workflows/ci-windows.yml
@@ -29,6 +29,11 @@ jobs:
arch: x86_64
msystem: ucrt64
toolchain: ucrt-x86_64
+ - name: Windows-ARM64
+ os: windows-11-arm
+ arch: aarch64
+ msystem: clangarm64
+ toolchain: clang-aarch64
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -46,7 +51,9 @@ jobs:
- name: Update Windows dependencies
env:
- MSYSTEM: ${{ matrix.msystem }}
+ # MSYSTEM is a built-in environment variable of MSYS2.
+ # Do not use this environment variable name.
+ MATRIX_MSYSTEM: ${{ matrix.msystem }}
TOOLCHAIN: ${{ matrix.toolchain }}
shell: msys2 {0}
run: |
@@ -62,17 +69,22 @@ jobs:
"mingw-w64-${TOOLCHAIN}-curl-winssl"
"mingw-w64-${TOOLCHAIN}-gcc"
"mingw-w64-${TOOLCHAIN}-graphviz"
- "mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-miniupnpc"
"mingw-w64-${TOOLCHAIN}-nlohmann-json"
"mingw-w64-${TOOLCHAIN}-nodejs"
- "mingw-w64-${TOOLCHAIN}-nsis"
"mingw-w64-${TOOLCHAIN}-onevpl"
"mingw-w64-${TOOLCHAIN}-openssl"
"mingw-w64-${TOOLCHAIN}-opus"
"mingw-w64-${TOOLCHAIN}-toolchain"
)
+ if [[ "${MATRIX_MSYSTEM}" == "ucrt64" ]]; then
+ dependencies+=(
+ "mingw-w64-${TOOLCHAIN}-MinHook"
+ "mingw-w64-${TOOLCHAIN}-nsis"
+ )
+ fi
+
# do not modify below this line
ignore_packages=()
@@ -83,7 +95,7 @@ jobs:
tarball="${pkg}-${version}-any.pkg.tar.zst"
# download working version
- wget "https://repo.msys2.org/mingw/${MSYSTEM}/${tarball}"
+ wget "https://repo.msys2.org/mingw/${MATRIX_MSYSTEM}/${tarball}"
tarballs="${tarballs} ${tarball}"
done
@@ -189,6 +201,11 @@ jobs:
mv ./cpack_artifacts/Sunshine.msi ../artifacts/Sunshine-${{ matrix.name }}-installer.msi
mv ./cpack_artifacts/Sunshine.zip ../artifacts/Sunshine-${{ matrix.name }}-portable.zip
+ - name: Debug nsis
+ if: always()
+ shell: msys2 {0}
+ run: cat ./build/cpack_artifacts/_CPack_Packages/win64/NSIS/NSISOutput.log || true
+
- name: Debug wix
if: always()
shell: msys2 {0}
diff --git a/cmake/compile_definitions/windows.cmake b/cmake/compile_definitions/windows.cmake
index c1a2c95fc45..57af5e2a435 100644
--- a/cmake/compile_definitions/windows.cmake
+++ b/cmake/compile_definitions/windows.cmake
@@ -9,6 +9,13 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
# gcc complains about misleading indentation in some mingw includes
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-misleading-indentation)
+# Disable warnings for Windows ARM64
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
+ list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-dll-attribute-on-redeclaration) # Boost
+ list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-unknown-warning-option) # ViGEmClient
+ list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-unused-variable) # Boost
+endif()
+
# see gcc bug 98723
add_definitions(-DUSE_BOOST_REGEX)
diff --git a/cmake/dependencies/windows.cmake b/cmake/dependencies/windows.cmake
index 3faad7dfd41..a9e5630d971 100644
--- a/cmake/dependencies/windows.cmake
+++ b/cmake/dependencies/windows.cmake
@@ -1,9 +1,35 @@
# windows specific dependencies
-# Make sure MinHook is installed
-find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED)
-find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED)
+# MinHook setup - use installed minhook for AMD64, otherwise download minhook-detours for ARM64
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
+ # Make sure MinHook is installed for x86/x64
+ find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED)
+ find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED)
-add_library(minhook::minhook STATIC IMPORTED)
-set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY})
-target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR})
+ add_library(minhook::minhook STATIC IMPORTED)
+ set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY})
+ target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR})
+else()
+ # Download pre-built minhook-detours for ARM64
+ message(STATUS "Downloading minhook-detours pre-built binaries for ARM64")
+ include(FetchContent)
+
+ FetchContent_Declare(
+ minhook-detours
+ URL https://github.com/m417z/minhook-detours/releases/download/v1.0.6/minhook-detours-1.0.6.zip
+ URL_HASH SHA256=E719959D824511E27395A82AEDA994CAAD53A67EE5894BA5FC2F4BF1FA41E38E
+ )
+ FetchContent_MakeAvailable(minhook-detours)
+
+ # Create imported library for the pre-built DLL
+ set(_MINHOOK_DLL
+ "${minhook-detours_SOURCE_DIR}/Release/minhook-detours.ARM64.Release.dll"
+ CACHE INTERNAL "Path to minhook-detours DLL")
+ add_library(minhook::minhook SHARED IMPORTED GLOBAL)
+ set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION "${_MINHOOK_DLL}")
+ set_property(TARGET minhook::minhook PROPERTY IMPORTED_IMPLIB
+ "${minhook-detours_SOURCE_DIR}/Release/minhook-detours.ARM64.Release.lib")
+ set_target_properties(minhook::minhook PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${minhook-detours_SOURCE_DIR}/src"
+ )
+endif()
diff --git a/cmake/packaging/windows.cmake b/cmake/packaging/windows.cmake
index a3718f7531b..9d75f7a694a 100644
--- a/cmake/packaging/windows.cmake
+++ b/cmake/packaging/windows.cmake
@@ -4,6 +4,11 @@ install(TARGETS sunshine RUNTIME DESTINATION "." COMPONENT application)
# Hardening: include zlib1.dll (loaded via LoadLibrary() in openssl's libcrypto.a)
install(FILES "${ZLIB}" DESTINATION "." COMPONENT application)
+# ARM64: include minhook-detours DLL (shared library for ARM64)
+if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND DEFINED _MINHOOK_DLL)
+ install(FILES "${_MINHOOK_DLL}" DESTINATION "." COMPONENT application)
+endif()
+
# ViGEmBus installer
set(VIGEMBUS_INSTALLER "${CMAKE_BINARY_DIR}/scripts/vigembus_installer.exe")
set(VIGEMBUS_DOWNLOAD_URL_1 "https://github.com/nefarius/ViGEmBus/releases/download")
diff --git a/cmake/packaging/windows_nsis.cmake b/cmake/packaging/windows_nsis.cmake
index ed37bbbbb3e..7c413b9f8d2 100644
--- a/cmake/packaging/windows_nsis.cmake
+++ b/cmake/packaging/windows_nsis.cmake
@@ -3,12 +3,18 @@
set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}")
+# Enable detailed logging only on AMD64
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
+ set(NSIS_LOGSET_COMMAND "LogSet on")
+else()
+ set(NSIS_LOGSET_COMMAND "")
+endif()
+
# Extra install commands
# Runs the main setup script which handles all installation tasks
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
- ; Enable detailed logging
- LogSet on
+ ${NSIS_LOGSET_COMMAND}
IfSilent +3 0
nsExec::ExecToLog \
'powershell -ExecutionPolicy Bypass \
@@ -24,8 +30,7 @@ SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
# Runs the main setup script which handles all uninstallation tasks
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
- ; Enable detailed logging
- LogSet on
+ ${NSIS_LOGSET_COMMAND}
nsExec::ExecToLog \
'powershell -ExecutionPolicy Bypass \
-File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action uninstall'
diff --git a/docs/building.md b/docs/building.md
index b3474758e0e..69d8b043b0b 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -126,33 +126,53 @@ sudo port install "${dependencies[@]}"
```
#### Windows
-First you need to install [MSYS2](https://www.msys2.org), then startup "MSYS2 UCRT64" and execute the following
-commands.
+
+> [!WARNING]
+> Cross-compilation is not supported on Windows. You must build on the target architecture.
+
+First, you need to install [MSYS2](https://www.msys2.org).
+
+For AMD64 startup "MSYS2 UCRT64" (or for ARM64 startup "MSYS2 CLANGARM64") then execute the following commands.
##### Update all packages
```bash
pacman -Syu
```
+##### Set toolchain variable
+For UCRT64:
+```bash
+export TOOLCHAIN="ucrt-x86_64"
+```
+
+For CLANGARM64:
+```bash
+export TOOLCHAIN="clang-aarch64"
+```
+
##### Install dependencies
```bash
dependencies=(
"git"
- "mingw-w64-ucrt-x86_64-boost" # Optional
- "mingw-w64-ucrt-x86_64-cmake"
- "mingw-w64-ucrt-x86_64-cppwinrt"
- "mingw-w64-ucrt-x86_64-curl-winssl"
- "mingw-w64-ucrt-x86_64-doxygen" # Optional, for docs... better to install official Doxygen
- "mingw-w64-ucrt-x86_64-graphviz" # Optional, for docs
- "mingw-w64-ucrt-x86_64-MinHook"
- "mingw-w64-ucrt-x86_64-miniupnpc"
- "mingw-w64-ucrt-x86_64-nodejs"
- "mingw-w64-ucrt-x86_64-nsis"
- "mingw-w64-ucrt-x86_64-onevpl"
- "mingw-w64-ucrt-x86_64-openssl"
- "mingw-w64-ucrt-x86_64-opus"
- "mingw-w64-ucrt-x86_64-toolchain"
+ "mingw-w64-${TOOLCHAIN}-boost" # Optional
+ "mingw-w64-${TOOLCHAIN}-cmake"
+ "mingw-w64-${TOOLCHAIN}-cppwinrt"
+ "mingw-w64-${TOOLCHAIN}-curl-winssl"
+ "mingw-w64-${TOOLCHAIN}-doxygen" # Optional, for docs... better to install official Doxygen
+ "mingw-w64-${TOOLCHAIN}-graphviz" # Optional, for docs
+ "mingw-w64-${TOOLCHAIN}-miniupnpc"
+ "mingw-w64-${TOOLCHAIN}-nodejs"
+ "mingw-w64-${TOOLCHAIN}-onevpl"
+ "mingw-w64-${TOOLCHAIN}-openssl"
+ "mingw-w64-${TOOLCHAIN}-opus"
+ "mingw-w64-${TOOLCHAIN}-toolchain"
)
+if [[ "${MSYSTEM}" == "UCRT64" ]]; then
+ dependencies+=(
+ "mingw-w64-${TOOLCHAIN}-MinHook"
+ "mingw-w64-${TOOLCHAIN}-nsis"
+ )
+fi
pacman -S "${dependencies[@]}"
```
diff --git a/docs/getting_started.md b/docs/getting_started.md
index d69a309c4d0..106e077a68d 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -329,21 +329,30 @@ brew uninstall sunshine
### Windows
+> [!NOTE]
+> Sunshine supports ARM64 on Windows; however, this should be considered experimental. This version does not properly
+> support GPU scheduling and any hardware acceleration.
+
#### Installer (recommended)
> [!CAUTION]
> The msi installer is preferred moving forward. Before using a different type of installer, you should manually
> uninstall the previous installation.
-1. Download and install
- [Sunshine-Windows-AMD64-installer.msi](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.msi)
- [Sunshine-Windows-AMD64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.exe)
+1. Download and install based on your architecture:
+
+ | Architecture | Installer |
+ |-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
+ | AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-installer.msi](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.msi) |
+ | AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.exe) |
+ | ARM64 | [Sunshine-Windows-ARM64-installer.msi](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-installer.msi) |
+ | ARM64 | [Sunshine-Windows-ARM64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-installer.exe) |
> [!TIP]
> Installer logs can be found in the following locations.
> | File | log paths |
> | ---- | --------- |
-> | .exe | `%%PROGRAMFILES%/Sunshine/install.log`
`%%TEMP%/Sunshine/logs/install/` |
+> | .exe | `%%PROGRAMFILES%/Sunshine/install.log` (AMD64 only)
`%%TEMP%/Sunshine/logs/install/` |
> | .msi | `%%TEMP%/Sunshine/logs/install/` |
> [!CAUTION]
@@ -359,8 +368,13 @@ overflow menu. Different versions of Windows may provide slightly different step
> By using this package instead of the installer, performance will be reduced. This package is not
> recommended for most users. No support will be provided!
-1. Download and extract
- [Sunshine-Windows-AMD64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-portable.zip)
+1. Download and extract based on your architecture:
+
+ | Architecture | Installer |
+ |-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
+ | AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-portable.zip) |
+ | ARM64 | [Sunshine-Windows-ARM64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-portable.zip) |
+
2. Open command prompt as administrator
3. Firewall rules
diff --git a/package.json b/package.json
index 065fc6d3340..56df3046fff 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,10 @@
"@vitejs/plugin-vue": "6.0.1",
"serve": "14.2.5",
"vite": "6.3.6",
- "vite-plugin-ejs": "1.7.0"
+ "vite-plugin-ejs": "1.7.0",
+ "@rollup/wasm-node": "4.57.1"
+ },
+ "overrides": {
+ "rollup": "npm:@rollup/wasm-node@4.57.1"
}
}
diff --git a/scripts/linux_build.sh b/scripts/linux_build.sh
index 2b1d705ee7b..312c74fdec5 100755
--- a/scripts/linux_build.sh
+++ b/scripts/linux_build.sh
@@ -695,7 +695,7 @@ elif grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="13"
- nvm_node=0
+ nvm_node=1
elif grep -q "Debian GNU/Linux 13 (trixie)" /etc/os-release; then
distro="debian"
version="13"
@@ -704,7 +704,7 @@ elif grep -q "Debian GNU/Linux 13 (trixie)" /etc/os-release; then
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="14"
- nvm_node=0
+ nvm_node=1
elif grep -q "PLATFORM_ID=\"platform:f41\"" /etc/os-release; then
distro="fedora"
version="41"
diff --git a/src/config.cpp b/src/config.cpp
index e5d58f3eccc..c320ed6dccf 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -187,7 +187,7 @@ namespace config {
};
template
- std::optional quality_from_view(const std::string_view &quality_type, const std::optional(&original)) {
+ ::std::optional quality_from_view(const ::std::string_view &quality_type, const ::std::optional(&original)) {
#define _CONVERT_(x) \
if (quality_type == #x##sv) \
return (int) T::x
@@ -199,7 +199,7 @@ namespace config {
}
template
- std::optional rc_from_view(const std::string_view &rc, const std::optional(&original)) {
+ ::std::optional rc_from_view(const ::std::string_view &rc, const ::std::optional(&original)) {
#define _CONVERT_(x) \
if (rc == #x##sv) \
return (int) T::x
@@ -212,7 +212,7 @@ namespace config {
}
template
- std::optional usage_from_view(const std::string_view &usage, const std::optional(&original)) {
+ ::std::optional usage_from_view(const ::std::string_view &usage, const ::std::optional(&original)) {
#define _CONVERT_(x) \
if (usage == #x##sv) \
return (int) T::x
@@ -225,7 +225,7 @@ namespace config {
return original;
}
- int coder_from_view(const std::string_view &coder) {
+ int coder_from_view(const ::std::string_view &coder) {
if (coder == "auto"sv) {
return _auto;
}
diff --git a/src/nvenc/nvenc_d3d11_on_cuda.h b/src/nvenc/nvenc_d3d11_on_cuda.h
index 102e18097d1..80aeb9ed8f9 100644
--- a/src/nvenc/nvenc_d3d11_on_cuda.h
+++ b/src/nvenc/nvenc_d3d11_on_cuda.h
@@ -56,7 +56,6 @@ namespace nvenc {
autopop_context push_context();
- HMODULE dll = nullptr;
const ID3D11DevicePtr d3d_device;
ID3D11Texture2DPtr d3d_input_texture;
diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp
index d7d4680edfd..b26f91a811b 100644
--- a/src/platform/windows/audio.cpp
+++ b/src/platform/windows/audio.cpp
@@ -32,14 +32,14 @@ DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64)
#define STEAM_DRIVER_SUBDIR L"x64"
-#else
- #warning No known Steam audio driver for this architecture
#endif
namespace {
constexpr auto SAMPLE_RATE = 48000;
+#ifdef STEAM_DRIVER_SUBDIR
constexpr auto STEAM_AUDIO_DRIVER_PATH = L"%CommonProgramFiles(x86)%\\Steam\\drivers\\Windows10\\" STEAM_DRIVER_SUBDIR L"\\SteamStreamingSpeakers.inf";
+#endif
constexpr auto waveformat_mask_stereo = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp
index c6b694f0a35..52a87c7817d 100644
--- a/src/platform/windows/display_vram.cpp
+++ b/src/platform/windows/display_vram.cpp
@@ -1907,6 +1907,12 @@ namespace platf::dxgi {
if (!boost::algorithm::ends_with(name, "_nvenc")) {
return false;
}
+ } else if (adapter_desc.VendorId == 0x4D4F4351 || // Qualcomm (QCOM as MOQC reversed)
+ adapter_desc.VendorId == 0x5143) { // Qualcomm alternate ID
+ // If it's not a MediaFoundation encoder, it's not compatible with a Qualcomm GPU
+ if (!boost::algorithm::ends_with(name, "_mf")) {
+ return false;
+ }
} else {
BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view();
}
diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp
index 85273609ec5..533e3790013 100644
--- a/src/platform/windows/input.cpp
+++ b/src/platform/windows/input.cpp
@@ -10,6 +10,7 @@
// standard includes
#include
#include
+#include
// lib includes
#include
@@ -1134,9 +1135,9 @@ namespace platf {
void unicode(input_t &input, char *utf8, int size) {
// We can do no worse than one UTF-16 character per byte of UTF-8
- WCHAR wide[size];
+ std::vector wide(size);
- int chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size, wide, size);
+ int chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size, wide.data(), size);
if (chars <= 0) {
return;
}
diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp
index 5a4ea26e709..4736a4c731c 100644
--- a/src/platform/windows/misc.cpp
+++ b/src/platform/windows/misc.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
// lib includes
#include
@@ -1383,7 +1384,7 @@ namespace platf {
auto const max_bufs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0);
- WSABUF bufs[(send_info.headers ? send_info.block_count : 1) * max_bufs_per_msg];
+ std::vector bufs((send_info.headers ? send_info.block_count : 1) * max_bufs_per_msg);
DWORD bufcount = 0;
if (send_info.headers) {
// Interleave buffers for headers and payloads
@@ -1409,7 +1410,7 @@ namespace platf {
}
}
- msg.lpBuffers = bufs;
+ msg.lpBuffers = bufs.data();
msg.dwBufferCount = bufcount;
msg.dwFlags = 0;
diff --git a/src/platform/windows/windows.rc b/src/platform/windows/windows.rc
index 8f1c2bf767a..417fcf0d047 100644
--- a/src/platform/windows/windows.rc
+++ b/src/platform/windows/windows.rc
@@ -41,4 +41,4 @@ BEGIN
END
END
-SuperDuperAmazing ICON DISCARDABLE PROJECT_ICON_PATH
+SuperDuperAmazing ICON DISCARDABLE TOSTRING(PROJECT_ICON_PATH)
diff --git a/src/video.cpp b/src/video.cpp
index 0290fd95002..7487e1278e6 100644
--- a/src/video.cpp
+++ b/src/video.cpp
@@ -300,6 +300,7 @@ namespace video {
ALWAYS_REPROBE = 1 << 9, ///< This is an encoder of last resort and we want to aggressively probe for a better one
YUV444_SUPPORT = 1 << 10, ///< Encoder may support 4:4:4 chroma sampling depending on hardware
ASYNC_TEARDOWN = 1 << 11, ///< Encoder supports async teardown on a different thread
+ FIXED_GOP_SIZE = 1 << 12, ///< Use fixed small GOP size (encoder doesn't support on-demand IDR frames)
};
class avcodec_encode_session_t: public encode_session_t {
@@ -825,6 +826,63 @@ namespace video {
},
PARALLEL_ENCODING
};
+
+ encoder_t mediafoundation {
+ "mediafoundation"sv,
+ std::make_unique(
+ AV_HWDEVICE_TYPE_D3D11VA,
+ AV_HWDEVICE_TYPE_NONE,
+ AV_PIX_FMT_D3D11,
+ AV_PIX_FMT_NV12, // SDR 4:2:0 8-bit (only format Qualcomm supports)
+ AV_PIX_FMT_NONE, // No HDR - Qualcomm MF only supports 8-bit
+ AV_PIX_FMT_NONE, // No YUV444 SDR
+ AV_PIX_FMT_NONE, // No YUV444 HDR
+ dxgi_init_avcodec_hardware_input_buffer
+ ),
+ {
+ // Common options for AV1 - Qualcomm MF encoder
+ {
+ {"hw_encoding"s, 1},
+ {"rate_control"s, "cbr"s},
+ {"scenario"s, "display_remoting"s},
+ },
+ {}, // SDR-specific options
+ {}, // HDR-specific options
+ {}, // YUV444 SDR-specific options
+ {}, // YUV444 HDR-specific options
+ {}, // Fallback options
+ "av1_mf"s,
+ },
+ {
+ // Common options for HEVC - Qualcomm MF encoder
+ {
+ {"hw_encoding"s, 1},
+ {"rate_control"s, "cbr"s},
+ {"scenario"s, "display_remoting"s},
+ },
+ {}, // SDR-specific options
+ {}, // HDR-specific options
+ {}, // YUV444 SDR-specific options
+ {}, // YUV444 HDR-specific options
+ {}, // Fallback options
+ "hevc_mf"s,
+ },
+ {
+ // Common options for H.264 - Qualcomm MF encoder
+ {
+ {"hw_encoding"s, 1},
+ {"rate_control"s, "cbr"s},
+ {"scenario"s, "display_remoting"s},
+ },
+ {}, // SDR-specific options
+ {}, // HDR-specific options
+ {}, // YUV444 SDR-specific options
+ {}, // YUV444 HDR-specific options
+ {}, // Fallback options
+ "h264_mf"s,
+ },
+ PARALLEL_ENCODING | FIXED_GOP_SIZE // MF encoder doesn't support on-demand IDR frames
+ };
#endif
encoder_t software {
@@ -1031,6 +1089,7 @@ namespace video {
#ifdef _WIN32
&quicksync,
&amdvce,
+ &mediafoundation,
#endif
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
&vaapi,
@@ -1566,11 +1625,17 @@ namespace video {
ctx->max_b_frames = 0;
// Use an infinite GOP length since I-frames are generated on demand
- ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ?
- std::numeric_limits::max() :
- std::numeric_limits::max();
-
- ctx->keyint_min = std::numeric_limits::max();
+ // Exception: encoders with FIXED_GOP_SIZE flag don't support on-demand IDR
+ if (encoder.flags & FIXED_GOP_SIZE) {
+ // Fixed GOP for encoders that don't support on-demand IDR (e.g. Media Foundation)
+ ctx->gop_size = 120; // ~2 seconds at 60 FPS - larger to reduce oversized IDR frame frequency
+ ctx->keyint_min = 120;
+ } else {
+ ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ?
+ std::numeric_limits::max() :
+ std::numeric_limits::max();
+ ctx->keyint_min = std::numeric_limits::max();
+ }
// Some client decoders have limits on the number of reference frames
if (config.numRefFrames) {
diff --git a/src/video.h b/src/video.h
index 8dbf76e27bd..eb8bb46f0dd 100644
--- a/src/video.h
+++ b/src/video.h
@@ -220,6 +220,7 @@ namespace video {
#ifdef _WIN32
extern encoder_t amdvce;
extern encoder_t quicksync;
+ extern encoder_t mediafoundation;
#endif
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 9a570fd3e7f..1d9bdad4424 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -163,4 +163,15 @@ if (WIN32)
# prefer static libraries since we're linking statically
# this fixes libcurl linking errors when using non MSYS2 version of CMake
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
+
+ # Copy minhook-detours DLL to test binary directory for ARM64
+ if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND DEFINED _MINHOOK_DLL)
+ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${_MINHOOK_DLL}"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ COMMENT "Copying minhook-detours DLL to test binary directory"
+ VERBATIM
+ )
+ endif()
endif ()