Files
OpenIPC_RTL8812AU_Forword_cpp/CMakeLists.txt
T
2026-09-13 15:06:07 +08:00

377 lines
17 KiB
CMake
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
cmake_minimum_required(VERSION 3.16)
project(fpv_relay_cpp LANGUAGES C CXX)
# devourer(用户态 USB 驱动) 需要 C++20;本项目其余代码向后兼容,统一用 C++20。
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
# ---------------- 选项 ----------------
option(FPC_BUILD_WFB_CAPTURE "编译 wfb-ng pcap 采集模块(仅 Linux)" ON)
if(WIN32)
set(_fpc_usb_default ON)
else()
set(_fpc_usb_default OFF)
endif()
option(FPC_BUILD_WFB_USB "编译 devourer(USB) 无线采集/上行" ${_fpc_usb_default})
option(FPC_BUILD_GUI "编译 Qt 图形界面" OFF)
# Qt 安装前缀:由 CMakePresets.json 的 FPC_QT_PREFIX 指定(也可用 -D 覆盖)。
set(FPC_QT_PREFIX "" CACHE PATH "Qt 安装前缀")
set(FPC_DEVOURER_GIT_TAG "30d248e021f35be0a8fe85c84939a57a45756901" CACHE STRING "devourer 固定 commit")
# 本地 devourer 源码目录(可选):设了就用本地,否则联网 FetchContent 拉取。
# 也支持把源码放到 3rd/devourer(存在即自动使用,免联网)。
set(FPC_DEVOURER_SRC "" CACHE PATH "本地 devourer 源码目录(可选)")
# 编译产物输出目录:所有可执行文件/运行库/示例配置都汇总到这里
set(FPC_DIST_DIR "${CMAKE_BINARY_DIR}/dist" CACHE PATH "编译产物输出目录")
# Windows 运行期依赖(libusb/libsodium 的 bin/include/lib)与 ffmpeg.exe(预览解码)。
# 默认使用仓库内 vendored 的 3rd/win-deps 与 3rd/ffmpeg;也可用环境变量
# FPV_DEPS_PREFIX / FFMPEG_EXE 或 -D 覆盖。
set(FPC_DEPS_PREFIX "$ENV{FPV_DEPS_PREFIX}" CACHE PATH "libusb/libsodium 前缀(Windows)")
if(NOT FPC_DEPS_PREFIX AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/3rd/win-deps/include/libusb-1.0/libusb.h")
set(FPC_DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/3rd/win-deps" CACHE PATH "libusb/libsodium 前缀(Windows)" FORCE)
endif()
set(FPC_FFMPEG_EXE "$ENV{FFMPEG_EXE}" CACHE FILEPATH "ffmpeg.exe 路径(视频预览解码)")
if(NOT FPC_FFMPEG_EXE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/3rd/ffmpeg/ffmpeg.exe")
set(FPC_FFMPEG_EXE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/ffmpeg/ffmpeg.exe" CACHE FILEPATH "ffmpeg.exe 路径(视频预览解码)" FORCE)
endif()
# ---------------- 第三方库 ----------------
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "" FORCE)
set(YAML_CPP_INSTALL OFF CACHE BOOL "" FORCE)
add_subdirectory(3rd/yaml-cpp EXCLUDE_FROM_ALL)
set(SPDLOG_BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_BENCH OFF CACHE BOOL "" FORCE)
add_subdirectory(3rd/spdlog EXCLUDE_FROM_ALL)
add_library(cli11 INTERFACE)
target_include_directories(cli11 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/3rd/CLI11/include)
# ---------------- devourer(用户态 USB 无线驱动,仅 Jaguar1=RTL8812AU) ----------------
set(FPC_HAVE_WFB_USB OFF)
if(FPC_BUILD_WFB_USB)
# 定位 libusb / libsodium。
# Windows 上不依赖 pkg-config:直接从 FPC_DEPS_PREFIX 手动查找,
# 并提供一个 PkgConfig::libusb 导入目标供 devourer 的 CMake 复用。
if(WIN32 AND FPC_DEPS_PREFIX)
find_path(FPC_LIBUSB_INCLUDE_DIR libusb-1.0/libusb.h
HINTS "${FPC_DEPS_PREFIX}/include")
find_library(FPC_LIBUSB_LIBRARY NAMES usb-1.0 libusb-1.0
HINTS "${FPC_DEPS_PREFIX}/lib")
find_path(FPC_SODIUM_INCLUDE_DIR sodium.h HINTS "${FPC_DEPS_PREFIX}/include")
find_library(FPC_SODIUM_LIBRARY NAMES sodium libsodium HINTS "${FPC_DEPS_PREFIX}/lib")
if(NOT FPC_LIBUSB_LIBRARY OR NOT FPC_SODIUM_LIBRARY)
message(FATAL_ERROR
"未在 FPC_DEPS_PREFIX=${FPC_DEPS_PREFIX} 找到 libusb/libsodium。"
"请设置环境变量 FPV_DEPS_PREFIX 指向含 include/lib/bin 的前缀目录。")
endif()
add_library(PkgConfig::libusb INTERFACE IMPORTED)
target_include_directories(PkgConfig::libusb INTERFACE "${FPC_LIBUSB_INCLUDE_DIR}")
target_link_libraries(PkgConfig::libusb INTERFACE "${FPC_LIBUSB_LIBRARY}")
set(SODIUM_INCLUDE_DIRS "${FPC_SODIUM_INCLUDE_DIR}")
set(SODIUM_LIBRARIES "${FPC_SODIUM_LIBRARY}")
set(SODIUM_LIBRARY_DIRS "")
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBUSB REQUIRED IMPORTED_TARGET libusb-1.0)
pkg_check_modules(SODIUM REQUIRED libsodium)
endif()
# 只编 RTL8812AU/8811AU/8821AU(Jaguar1),其余后端关闭以缩小体积与依赖。
set(DEVOURER_JAGUAR1 ON CACHE BOOL "" FORCE)
set(DEVOURER_8814 OFF CACHE BOOL "" FORCE)
set(DEVOURER_JAGUAR2_8822B OFF CACHE BOOL "" FORCE)
set(DEVOURER_JAGUAR2_8821C OFF CACHE BOOL "" FORCE)
set(DEVOURER_JAGUAR3_8822C OFF CACHE BOOL "" FORCE)
set(DEVOURER_JAGUAR3_8822E OFF CACHE BOOL "" FORCE)
set(DEVOURER_8733B OFF CACHE BOOL "" FORCE)
set(DEVOURER_KESTREL_8852B OFF CACHE BOOL "" FORCE)
set(DEVOURER_KESTREL_8852C OFF CACHE BOOL "" FORCE)
set(DEVOURER_MT7612U OFF CACHE BOOL "" FORCE)
set(DEVOURER_PCIE OFF CACHE BOOL "" FORCE)
include(FetchContent)
# CMake 4.x:允许 FetchContent_Populate(不把 devourer 的示例/测试加进默认构建)
if(POLICY CMP0169)
cmake_policy(SET CMP0169 OLD)
endif()
# 1) 本地源码优先:FPC_DEVOURER_SRC,或仓库内 3rd/devourer
if(NOT FPC_DEVOURER_SRC AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/3rd/devourer/CMakeLists.txt")
set(FPC_DEVOURER_SRC "${CMAKE_CURRENT_SOURCE_DIR}/3rd/devourer")
endif()
# 2) 清掉失效的 FETCHCONTENT_SOURCE_DIR_DEVOURER(指向已删除的目录会让 configure 报错)
if(DEFINED FETCHCONTENT_SOURCE_DIR_DEVOURER AND NOT EXISTS "${FETCHCONTENT_SOURCE_DIR_DEVOURER}")
unset(FETCHCONTENT_SOURCE_DIR_DEVOURER CACHE)
endif()
if(FPC_DEVOURER_SRC AND EXISTS "${FPC_DEVOURER_SRC}/CMakeLists.txt")
message(STATUS "使用本地 devourer: ${FPC_DEVOURER_SRC}")
add_subdirectory("${FPC_DEVOURER_SRC}" "${CMAKE_BINARY_DIR}/devourer-build" EXCLUDE_FROM_ALL)
else()
FetchContent_Declare(devourer
GIT_REPOSITORY https://github.com/openipc/devourer.git
GIT_TAG ${FPC_DEVOURER_GIT_TAG}
GIT_SHALLOW TRUE)
FetchContent_GetProperties(devourer)
if(NOT devourer_POPULATED)
FetchContent_Populate(devourer)
endif()
add_subdirectory(${devourer_SOURCE_DIR} ${devourer_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
set(FPC_HAVE_WFB_USB ON)
endif()
# ---------------- wfb-ng 静态库(FEC/解密/加密) ----------------
# Linux pcap 采集版:含 tx.cpp(AF_PACKET 注入)。
if(FPC_BUILD_WFB_CAPTURE AND UNIX AND NOT APPLE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SODIUM REQUIRED libsodium)
pkg_check_modules(PCAP REQUIRED libpcap)
add_library(wfbng STATIC
3rd/wfb-ng/src/rx.cpp
3rd/wfb-ng/src/tx.cpp
3rd/wfb-ng/src/wifibroadcast.cpp
3rd/wfb-ng/src/radiotap.c
3rd/wfb-ng/src/zfex.c
)
target_compile_definitions(wfbng PRIVATE __WFB_RX_SHARED_LIBRARY__ FPC_WFB_TX_NO_MAIN)
target_include_directories(wfbng PUBLIC
3rd/wfb-ng/src
${SODIUM_INCLUDE_DIRS}
${PCAP_INCLUDE_DIRS}
)
target_link_libraries(wfbng PUBLIC ${SODIUM_LIBRARIES} ${PCAP_LIBRARIES})
# wfb-ng 对报文做了大量指针类型转换,必须关闭严格别名优化,否则解密会失败
target_compile_options(wfbng PRIVATE -w -fno-strict-aliasing)
set(FPC_HAVE_WFB_CAPTURE ON)
endif()
# Windows(或仅 USB)版:只编 Aggregator,不含 pcap Receiver / AF_PACKET tx。
if(FPC_HAVE_WFB_USB AND NOT TARGET wfbng)
add_library(wfbng STATIC
3rd/wfb-ng/src/rx.cpp
3rd/wfb-ng/src/wifibroadcast.cpp
3rd/wfb-ng/src/radiotap.c
3rd/wfb-ng/src/zfex.c
)
target_compile_definitions(wfbng PRIVATE __WFB_RX_SHARED_LIBRARY__)
target_include_directories(wfbng PUBLIC 3rd/wfb-ng/src ${SODIUM_INCLUDE_DIRS})
target_link_directories(wfbng PUBLIC ${SODIUM_LIBRARY_DIRS})
target_link_libraries(wfbng PUBLIC ${SODIUM_LIBRARIES})
if(NOT MSVC)
target_compile_options(wfbng PRIVATE -w -fno-strict-aliasing)
endif()
endif()
# ---------------- 核心库(采集/转发/录制/RTSP/状态) ----------------
set(FPC_CORE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/capture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/radio.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/udp_capture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/wfb_uplink.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/config/config.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/relay/dvr_sink.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/relay/h265.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/relay/stream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/relay/udp_sink.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtsp/rtsp_server.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/status/http_server.cpp
)
# Linux pcap 采集(wfb_capture.cpp 依赖 pcap/radiotap)。
if(FPC_HAVE_WFB_CAPTURE AND NOT WIN32)
list(APPEND FPC_CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/capture/wfb_capture.cpp)
endif()
# USB(devourer) 采集 + 上行。
if(FPC_HAVE_WFB_USB)
list(APPEND FPC_CORE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/wfb_usb_device.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/wfb_usb_capture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capture/wfb_usb_tx.cpp
)
endif()
add_library(fpv_core STATIC ${FPC_CORE_SRC})
target_include_directories(fpv_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(TARGET yaml-cpp::yaml-cpp)
set(FPC_YAML_TARGET yaml-cpp::yaml-cpp)
else()
set(FPC_YAML_TARGET yaml-cpp)
endif()
find_package(Threads REQUIRED)
target_link_libraries(fpv_core PUBLIC ${FPC_YAML_TARGET} spdlog::spdlog Threads::Threads)
if(FPC_HAVE_WFB_CAPTURE)
target_compile_definitions(fpv_core PUBLIC FPC_HAVE_WFB_CAPTURE=1)
target_link_libraries(fpv_core PUBLIC wfbng)
endif()
if(FPC_HAVE_WFB_USB)
target_compile_definitions(fpv_core PUBLIC FPC_HAVE_WFB_USB=1)
target_link_libraries(fpv_core PUBLIC wfbng devourer)
endif()
# Windows 平台:统一编译宏 + 链接 Winsock。
if(WIN32)
target_compile_definitions(fpv_core PUBLIC
WIN32_LEAN_AND_MEAN
NOMINMAX
_WIN32_WINNT=0x0601
_CRT_SECURE_NO_WARNINGS
)
target_link_libraries(fpv_core PUBLIC ws2_32)
# 强制 winsock2.h 先于 windows.h(spdlog/Qt/devourer 会间接包含 windows.h)。
if(MSVC)
target_compile_options(fpv_core PUBLIC /FIwinsock2.h)
else()
target_compile_options(fpv_core PUBLIC -include winsock2.h)
endif()
endif()
# ============================================================================
# 编译产物汇总到 FPC_DIST_DIR:可执行文件 + 运行期依赖 + 示例配置 + 密钥
# ============================================================================
set(FPC_STAGE_CMDS
COMMAND ${CMAKE_COMMAND} -E make_directory "${FPC_DIST_DIR}"
)
# 示例配置与密钥
foreach(_f config.example.yaml config.windows.example.yaml)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_f}")
list(APPEND FPC_STAGE_CMDS
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/${_f}" "${FPC_DIST_DIR}/")
endif()
endforeach()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ass/gs.key")
list(APPEND FPC_STAGE_CMDS
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/ass/gs.key" "${FPC_DIST_DIR}/gs.key")
endif()
# 本地 config.yaml(若存在,未被 git 跟踪)也一并带到 dist,方便直接用
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.yaml")
list(APPEND FPC_STAGE_CMDS
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/config.yaml" "${FPC_DIST_DIR}/config.yaml")
endif()
# Windows 运行期依赖 DLL(libusb/libsodium/ffmpeg)
if(WIN32)
set(_fpc_dep_dlls)
if(FPC_DEPS_PREFIX)
file(GLOB _fpc_dep_dlls
"${FPC_DEPS_PREFIX}/bin/libusb*.dll"
"${FPC_DEPS_PREFIX}/bin/libsodium*.dll")
endif()
if(FPC_FFMPEG_EXE AND EXISTS "${FPC_FFMPEG_EXE}")
list(APPEND _fpc_dep_dlls "${FPC_FFMPEG_EXE}")
endif()
if(_fpc_dep_dlls)
list(APPEND FPC_STAGE_CMDS
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_fpc_dep_dlls} "${FPC_DIST_DIR}/")
endif()
endif()
# ---------------- 命令行程序 ----------------
add_executable(fpv-relay src/main.cpp)
target_link_libraries(fpv-relay PRIVATE fpv_core cli11)
# 可执行文件直接输出到 dist,和运行期依赖放在一起(IDE 直接运行也能找到 DLL)
set_target_properties(fpv-relay PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${FPC_DIST_DIR}")
add_custom_command(TARGET fpv-relay POST_BUILD ${FPC_STAGE_CMDS})
# ---------------- Qt 图形界面(可选) ----------------
if(FPC_BUILD_GUI)
list(APPEND CMAKE_PREFIX_PATH ${FPC_QT_PREFIX})
find_package(Qt6 REQUIRED COMPONENTS Quick QuickControls2 Svg Widgets Network PrintSupport)
qt_standard_project_setup()
set(FLUENTUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(FLUENTUI_BUILD_STATIC_LIB ON CACHE BOOL "" FORCE)
add_subdirectory(3rd/FluentUI EXCLUDE_FROM_ALL)
# 视频预览不再依赖 GStreamer:运行时调用随程序分发的 ffmpeg.exe 解码
# (ffmpeg 能解 H264/H265,单文件自包含)。见 src/gui/video_item.cpp。
qt_add_executable(fpv-relay-gui
src/gui/main_gui.cpp
src/gui/backend.cpp
src/gui/video_item.cpp
)
# 可执行文件直接输出到 dist,和 Qt/ffmpeg/libusb 等运行库放在一起
set_target_properties(fpv-relay-gui PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${FPC_DIST_DIR}")
target_link_libraries(fpv-relay-gui PRIVATE
fpv_core
fluentuiplugin
Qt6::Quick Qt6::QuickControls2 Qt6::Svg Qt6::Widgets Qt6::Network Qt6::PrintSupport
)
target_compile_definitions(fpv-relay-gui PRIVATE FLUENTUI_BUILD_STATIC_LIB)
qt_add_qml_module(fpv-relay-gui
URI FpvRelay
VERSION 1.0
QML_FILES
src/gui/qml/App.qml
src/gui/qml/DroneCard.qml
src/gui/qml/Settings.qml
)
# GUI 产物汇总(exe 与 FpvRelay QML 模块已随 RUNTIME_OUTPUT_DIRECTORY 落到 dist)
set(FPC_GUI_STAGE)
if(WIN32)
find_program(FPC_WINDEPLOYQT windeployqt HINTS "${FPC_QT_PREFIX}/bin")
if(FPC_WINDEPLOYQT)
list(APPEND FPC_GUI_STAGE
COMMAND "${FPC_WINDEPLOYQT}" --release
--qmldir "${CMAKE_CURRENT_SOURCE_DIR}/src/gui"
--dir "${FPC_DIST_DIR}" "${FPC_DIST_DIR}/fpv-relay-gui.exe")
endif()
# FluentUI 依赖的 Qt5Compat.GraphicalEffects + Qt6ShaderTools
if(EXISTS "${FPC_QT_PREFIX}/qml/Qt5Compat")
list(APPEND FPC_GUI_STAGE
COMMAND ${CMAKE_COMMAND} -E make_directory "${FPC_DIST_DIR}/qml"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${FPC_QT_PREFIX}/qml/Qt5Compat" "${FPC_DIST_DIR}/qml/Qt5Compat")
endif()
if(EXISTS "${FPC_QT_PREFIX}/bin/Qt6ShaderTools.dll")
list(APPEND FPC_GUI_STAGE
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${FPC_QT_PREFIX}/bin/Qt6ShaderTools.dll" "${FPC_DIST_DIR}/")
endif()
# FpvRelay QML 模块目录(生成在构建目录,拷贝到 dist)
# qt.conf:让 Qt 从程序目录找 QML
file(WRITE "${CMAKE_BINARY_DIR}/fpc_qt.conf" "[Paths]\nPrefix=.\nQmlImportsPath=qml\n")
list(APPEND FPC_GUI_STAGE
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/fpc_qt.conf" "${FPC_DIST_DIR}/qt.conf")
# windeployqt 拷的是 Qt 自带的 MinGW 运行库;若编译器版本不同,用编译器自带的覆盖。
if(MINGW)
get_filename_component(_fpc_mingw_bin "${CMAKE_CXX_COMPILER}" DIRECTORY)
foreach(_rt libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll)
if(EXISTS "${_fpc_mingw_bin}/${_rt}")
list(APPEND FPC_GUI_STAGE
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_fpc_mingw_bin}/${_rt}" "${FPC_DIST_DIR}/")
endif()
endforeach()
endif()
endif()
# 所有平台都把 FpvRelay QML 模块目录拷到 dist,保证从 dist 直接运行能找到模块
list(APPEND FPC_GUI_STAGE
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_BINARY_DIR}/FpvRelay" "${FPC_DIST_DIR}/FpvRelay")
# 依赖 DLL / 配置 / 密钥的汇总也挂到 GUI 目标,避免只构建 GUI 时缺失
add_custom_command(TARGET fpv-relay-gui POST_BUILD ${FPC_STAGE_CMDS} ${FPC_GUI_STAGE})
endif()