Veyra-NRVideo 1.4.1 Remote Play 串流构建与对应源码交付指南

原创2026-11-14 23:59:561,613 阅读
文章标签:音视频视频处理图像处理桌面应用直播

Veyra-NRVideo 1.4.1 Remote Play 串流构建与对应源码交付指南

本篇技术指南以 Veyra-NRVideo 1.4.1 的 PS5 串流发布说明为核心,完整拆解 VEYRA_ENABLE_REMOTEPLAY 这个可选构建开关背后的固定上游、受审补丁、依赖锁、双重构建流程与许可证交付义务。读完后你可以独立复现"先构建固定 Chiaki 原生检查,再构建带串流的产品包"这两步,并清楚知道每一步在什么前提下才算真正通过。

一、版本语义:1.4.1 便携包与默认构建的差别

原发布说明的第一句就是整个构建体系的入口:

The 1.4.1 portable package enables VEYRA_ENABLE_REMOTEPLAY. The default build without this option does not include PS5 Remote Play.

这句话有两个必须分开理解的事实:

  1. PS5 Remote Play 是可选组件,不是默认能力。仓库根 CMakeLists.txt 中该选项的默认值是 OFF:

    option(VEYRA_ENABLE_REMOTEPLAY "Build PS5 Remote Play with pinned Chiaki source" OFF)
    

    只有显式传入 -DVEYRA_ENABLE_REMOTEPLAY=ON 或使用构建包装脚本的 -RemotePlay 开关,串流源码、Chiaki 静态库和对应测试目标才会进入构建图。

  2. 1.4.1 便携包是从 main 构建的,构建前已合并隔离的颜色 / AverMedia / 导出分支;v1.4.1 标签在发布时才创建。也就是说"包内容"与"标签指向的提交"在时间上是解耦的,验证发布资产时必须按 docs/history/releases/RELEASE_1.4.1_EXECUTION.md 记录的发布提交与标签解析结果核对,而不是凭 main 的当前 HEAD 推断。

这个设计带来的实际后果是:关闭串流构建必须是"干净的 OFF",不能留下悬空的源码或后端组合。构建包装脚本在这一点上做了显式约束,见 scripts/build.ps1:

# Remote Play is explicit: no dangling source/backend combination and no
# silently substituted prebuilt Chiaki library. Shared CMake verifies the pin
# and complete reviewed patches before compiling the dependency from source.
$configureExtra += " -DVEYRA_ENABLE_REMOTEPLAY=" + $(if($RemotePlay){"ON"}else{"OFF"})

同一段代码还说明:不允许用预编译的 Chiaki 库静默替代源码构建。这条规则由共享 CMake 模块在配置阶段强制执行。

二、锁定的上游:chiaki-ng 提交与两个受审补丁

2.1 版本钉子

原文档给出的固定提交是 0e16950165f06e5c3291537c2eeba6e852be7120(来自 streetpea/chiaki-ng)。该值在仓库中被多处独立记录,形成交叉校验:

记录位置 作用
scripts/remoteplay/dependency-lock.json 声明性依赖锁:chiaki_commit、递归子模块要求、原生 API 扩展标记
scripts/remoteplay/verify-chiaki-stage.py 校验脚本内的 PIN 常量与补丁清单
scripts/remoteplay/build-native.ps1 构建脚本内的 $PinnedCommit
scripts/package-remoteplay-source.py 打包脚本内的 pin
cmake/VeyraRemotePlay.cmake 配置期调用校验脚本,失败即 FATAL_ERROR

依赖锁里还额外记录了三项容易被忽略的契约:

  • "recursive_submodules": "must match gitlinks in the pinned tree":子模块必须与固定树的 gitlink 完全一致;
  • "native_api": "CHIAKI_VEYRA_VIDEO_METADATA_API=1, reviewed metadata extension":视频元数据 API 是受审扩展,不是上游原生行为;
  • "veyra_reviewed_commit": "ff4122befc5909682d7a4df29da086d5f5bb619d":Veyra 侧"已评审组合"的提交标识,用于区分上游钉子和适配层状态。

2.2 补丁 0001:MSVC 兼容(五文件范围)

scripts/remoteplay/patches/0001-chiaki-msvc-vla-compat.patch 解决的是 MSVC 无法接受变长数组(VLA)一类 C 语言扩展的问题。依赖锁中明确列出它覆盖的五个文件:

"msvc_compatibility_scope": [
  "lib/src/ctrl.c",
  "lib/src/regist.c",
  "lib/src/remote/holepunch.c",
  "lib/src/remote/rudp.c",
  "lib/src/session.c"
]

从补丁内容可以读出它并非机械替换,而是把栈上 VLA 改成有边界检查的堆分配,例如 ctrl_message_send 中:

if(payload_size > SIZE_MAX - sizeof(header))
{
    free(enc);
    return CHIAKI_ERR_INVALID_DATA;
}
size_t buf_size = sizeof(header) + payload_size;
uint8_t *buf = malloc(buf_size);
if(!buf)
{
    free(enc);
    return CHIAKI_ERR_MEMORY;
}

补丁同时为若干帧解析路径补充了最小长度检查(如上例中对 message.data_size < 8 的拒绝分支)。这类改动会让"MSVC 能编译"与"输入被校验"同时成立,因此在评审时必须整体接受,不能只取其中一部分。

2.3 补丁 0002:视频元数据 API 扩展

scripts/remoteplay/patches/0002-chiaki-video-metadata.patch 增加了一个并行回调通道,而没有修改原有回调签名。核心是新增的能力宏与结构体:

#define CHIAKI_VEYRA_VIDEO_METADATA_API 1
typedef enum { CHIAKI_VEYRA_SAMPLE_CONFIG = 0, CHIAKI_VEYRA_SAMPLE_FRAME = 1 } ChiakiVeyraSampleKind;
typedef struct chiaki_veyra_video_sample_info_t
{
	ChiakiVeyraSampleKind kind;
	bool frame_index_valid;
	uint16_t frame_index;
	uint32_t width;
	uint32_t height;
	int32_t frames_lost;
	bool reference_recovered;
} ChiakiVeyraVideoSampleInfo;
typedef bool (*ChiakiVeyraVideoSampleCallback)(uint8_t *buf, size_t buf_size, const ChiakiVeyraVideoSampleInfo *info, void *user);

并在 chiaki_session_t 上追加 veyra_video_sample_cb / veyra_video_sample_cb_user 两个字段和 chiaki_session_set_veyra_video_sample_cb() 内联设置函数。在 videoreceiver.c 中,回调分发采用"新回调优先、旧回调兜底"的形式:

if(video_receiver->session->veyra_video_sample_cb)
{
	ChiakiVeyraVideoSampleInfo info = { 0 };
	info.kind = CHIAKI_VEYRA_SAMPLE_CONFIG;
	info.width = profile->width;
	info.height = profile->height;
	video_receiver->session->veyra_video_sample_cb(profile->header, profile->header_sz, &info, ...);
}
else if(video_receiver->session->video_sample_cb)
	video_receiver->session->video_sample_cb(profile->header, profile->header_sz, 0, false, ...);

这个设计对适配层的意义很直接:Veyra 可以拿到配置样本与帧样本的区分、真实帧号是否有效、编码分辨率、丢帧数与参考帧恢复标志,而不必从裸码流里猜。也正因为是 ABI 层面的结构体扩展,Veyra 侧文档 docs/remoteplay/NATIVE_GATE.md 特别强调"新 header 与库必须共同重建,不得混用旧二进制"。

2.4 校验脚本才是真正的防线

补丁"能应用"并不等于"树是干净的"。verify-chiaki-stage.py 对 clean 检出和 stage 两棵树同时检查 HEAD、暂存区、递归子模块与未跟踪文件,然后用临时 GIT_INDEX_FILE 重建期望树并做内容级比较:

with tempfile.TemporaryDirectory(prefix="veyra-chiaki-index-") as folder:
    env = dict(os.environ, GIT_INDEX_FILE=str(Path(folder) / "index"))
    git(stage, "read-tree", PIN, env=env)
    for patch in PATCHES:
        git(stage, "apply", "--cached", str(Path(__file__).resolve().parent / "patches" / patch), env=env)
    expected = git(stage, "write-tree", env=env)
    git(stage, "diff", "--exit-code", "--ignore-submodules=none", expected)
print("CHIAKI_STAGE_EXACT_PATCHES_VERIFIED")

比起"反向应用补丁是否成功",这种"重建 + 逐字节 diff"的做法能拒绝同一文件内的额外改动。脚本还在子模块遍历中对每个子模块执行 git diff --exit-code && git diff --cached --exit-code && test -z "$(git ls-files --others --exclude-standard)",避免脏子模块混入。

三、许可证与再分发边界

原文档在许可证上写得相当克制但完整:

The combined program is subject to Chiaki's AGPL-3.0-only license and OpenSSL exception as well as retained dependency notices; original Veyra source remains GPLv3.

落实到仓库中的事实:

  • licenses/remoteplay/CHIAKI_AGPL3_OPENSSL.txt 提供 AGPL-3.0 全文;
  • licenses/remoteplay/ 目录同时保留 SDL3_NOTICES.txt、curl-LICENSE.txt、nanopb-LICENSE.txt、jerasure-LICENSE.txt、gf-complete-LICENSE.txt,以及 json-c/、libevent/、miniupnpc/、openssl/、opus/、sdl3/ 六个子目录的 SPDX 与许可证记录;
  • THIRD_PARTY_NOTICES.md 说明派生补丁同样保留 AGPL-3.0-only 与上游 OpenSSL 例外,并明确"仅有 Veyra 的 GPL 文件不构成组合程序的完整许可记录"。

再分发边界的另一面是"不打包什么"。原文档说得很明确:

Windows networking, WinHTTP, DPAPI, WASAPI, DirectX, and GPU driver components are not redistributed.

以及源归档中不含 NVIDIA/Intel SDK、凭据、本地配置、日志或测试媒体。这与 scripts/package-remoteplay-source.py 的排除清单一致:

bad={'.dll','.exe','.lib','.pdb','.obj','.o','.a','.so','.dylib','.onnx','.pth','.pt','.zip','.7z','.mp4','.log','.addon64'}

从实现看,打包脚本对符号链接不是静默跳过,而是直接失败(Source symlink needs explicit review),要求人工确认。

四、源归档 Veyra-1.4.1-RemotePlay-source.zip 到底装了什么

原文档只用一段话描述这个资产,实际内容由打包脚本逐项构造。下表按 scripts/package-remoteplay-source.py 展开:

归档内路径 内容 生成方式
chiaki-clean/ 固定提交的 Chiaki 干净树(含递归子模块文件) 遍历 git ls-files,遇符号链接或排除扩展名则跳过/终止
chiaki-submodules.txt git submodule status --recursive 原始输出 直接写入,作为子模块钉子证据
veyra/scripts/remoteplay/ 两个受审补丁、校验脚本、dependency-lock.json、构建与测试脚本 目录整树复制
veyra/cmake/VeyraRemotePlay.cmake、veyra/scripts/build.ps1、veyra/CMakePresets.json 复现构建所需的共享 CMake 与包装脚本 单文件复制
veyra/docs/REMOTEPLAY_BUILD_<version>.md、veyra/docs/BUILD.md、veyra/THIRD_PARTY_NOTICES.md 构建说明与许可记录 单文件复制
licenses/remoteplay/ 依赖许可证与 notices 目录整树复制
dependencies/<dep>/source、/port、/installed-share json-c、libevent、miniupnpc、OpenSSL、Opus、SDL3 的依赖源码、vcpkg port 配方与 SPDX 从 vcpkg 构建树与 ports 目录复制
vcpkg-port-scripts/ vcpkg-cmake、vcpkg-cmake-config、vcpkg-cmake-get-vars 及 vcpkg 工具链脚本、triplet、LICENSE 目录整树复制
installed-status.txt vcpkg 已安装状态 单文件复制
fidelityfx/ 静态光流后端使用的开源 FidelityFX 源码 仅 FidelityFX-SDK 的 git ls-files
source-manifest.json 每个纳入文件的 path/size/sha256 打包时生成
excluded-development-binaries.json 被排除的开发期二进制清单 打包时生成
README.txt 版本、应用源码标签、FFmpeg 为独立资产、无专有 SDK/凭据 模板生成
<archive>.zip.sha256 侧车摘要文件(大写 SHA256 + 文件名) 打包后计算

打包脚本还有两条"证据强度"设计值得注意:

  1. port 配方与 SPDX 交叉校验:读取已安装 prefix 下的 share/<dep>/vcpkg.spdx.json,把其中 SPDXRef-port-file-* 的期望 SHA256 与实际 vcpkg/ports/<dep>/<fileName> 比对,不一致立即抛错。这保证归档里的 port 配方确实是构建时使用的版本。
  2. 依赖源码唯一性:sources=list((vcpkg/'buildtrees'/dep/'src').glob('*.clean')),并要求 len(sources)==1,出现多个候选直接以 Ambiguous source 失败,避免误打包旧版本。

原文档提到的 FFmpeg 说明在脚本里也有对应:归档 README 明确写 "FFmpeg source is a separate asset",即 FFmpeg 对应源码是另一个发布资产,不塞进本 ZIP。

1.4.1 的实际资产体积与摘要记录在 docs/history/releases/RELEASE_1.4.1_EXECUTION.md:Veyra-1.4.1-RemotePlay-source.zip 为 143753286 字节,SHA256 272E41BC128C9BD86D7A9A93E4F0150DD92DB6B9D00909643336B9E01561F166。该记录同时说明:源归档在构建文档更新后重新生成过,早期归档被移出最终上传目录,因此核对时只能使用最终目录中的那一份。

五、构建环境前置条件

原文档把工具链压缩成一句话:

To build: use Visual Studio 2022 x64 tools, CMake, Ninja, Python, protobuf tooling, the dependency paths recorded in the source archive, and separately licensed enhancement SDKs described in BUILD.md.

结合仓库实际内容,可拆成四类:

1) 基础工具链

工具 版本/形式要求 证据
Visual Studio 2022 x64 工具 需要 vcvars64.bat;脚本优先用 vswhere 定位,其次回退到 BuildTools scripts/build.ps1
CMake ≥ 3.24(预设文件声明),Ninja 生成器 CMakePresets.json
Python 3 CMake 以 find_package(Python3 REQUIRED COMPONENTS Interpreter) 强制要求,用于跑校验脚本 cmake/VeyraRemotePlay.cmake
protobuf 工具 PROTOC 必须指向真实 protoc.exe,缺省会配置失败 scripts/build.ps1
pkg-config 用于发现 json-c(pkg_check_modules(VEYRA_PSN_JSON REQUIRED IMPORTED_TARGET json-c)) cmake/VeyraRemotePlay.cmake

2) 静态依赖前缀:Opus、OpenSSL、json-c、libevent、miniupnpc、SDL3 必须与 /MT 静态运行时一致。构建脚本显式设置 CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded,CMake 侧则以 find_package(SDL3 3.4.14 EXACT CONFIG REQUIRED) 精确锁定 SDL3 版本(CMakeLists.txt),与依赖锁中 sdl3.version = 3.4.14 对齐。

3) 固定上游源码:干净检出 + 递归子模块,由第一步的 build-native.ps1 负责把它变成"打过补丁的 stage"。

4) 独立许可的增强 SDK:DLSS / NVOF / XeSS / FidelityFX / NVENC headers 等由 BUILD.md 描述,构建包装脚本按存在性自动开关。注意 scripts/build.ps1 的逻辑是"存在官方 SDK 树就开,否则显式 OFF",因此不配置增强 SDK 也能构建串流,只是产物不含对应增强。

docs/BUILD.md 在当前仓库快照中并不存在(docs/ 下只有 BUILD_2.1.1.md 至 BUILD_2.1.3*.md 等后续版本文档)。复现 1.4.1 时,构建参数请以 docs/history/releases/RELEASE_1.4.1_EXECUTION.md 记录的实机配置为准,并注意其中的路径是当时机器的绝对路径,换机必须重新定位。

六、第一步:用 build-native.ps1 构建固定 Chiaki

6.1 完整参数表

scripts/remoteplay/build-native.ps1 的参数与作用:

参数 必填 默认值 作用
-ChiakiCheckout 是 无 干净固定检出目录,只用于校验钉子与作为 worktree 源,不会被修改
-Root 否 脚本上两级目录 Veyra 仓库根
-StageDirectory 否 <Root>/out/remoteplay/chiaki-msvc-stage 打补丁的独立 git worktree 目录
-BuildDirectory 否 <Root>/out/remoteplay/native 原生探针构建目录
-PrefixPath 否 空 传给 CMAKE_PREFIX_PATH,即静态依赖安装前缀
-ToolchainFile 否 空 使用 vcpkg 时指向真实工具链文件
-ProtocPath 否 空 传给 PROTOC,必须是绝对路径(脚本会 Resolve-Path)
-PkgConfigPath 否 空 传给 PKG_CONFIG_EXECUTABLE
-Generator 否 Ninja CMake 生成器

6.2 调用示例

powershell.exe -NoProfile -File scripts/remoteplay/build-native.ps1 `
  -Root . `
  -ChiakiCheckout C:/veyra-deps/chiaki-source `
  -StageDirectory out/remoteplay/chiaki-msvc-stage `
  -BuildDirectory out/remoteplay/native `
  -PrefixPath C:/veyra-deps/remoteplay-installed/x64-windows-static `
  -ProtocPath C:/veyra-deps/remoteplay-installed/x64-windows/tools/protobuf/protoc.exe `
  -PkgConfigPath C:/veyra-deps/vcpkg/downloads/tools/msys2/<hash>/mingw64/bin/pkg-config.exe

-ProtocPath 与 -PkgConfigPath 不是可选点缀:docs/remoteplay/NATIVE_GATE.md 记录过一次"旧参数组没有 ProtocPath,在全新目录真实失败 Could not find protoc"的经历,并提醒不要因为某次成功 cache 就认为新目录可直接复现。

6.3 脚本内部到底做了七件事

  1. 解析并校验仓库根与检出目录,把 -Root 与 -ChiakiCheckout 都转成绝对路径。

  2. 钉住 HEAD:git rev-parse HEAD 必须等于 0e1695...,否则抛 Chiaki source pin mismatch。

  3. 要求干净:git status --porcelain --untracked-files=no 必须为空。

  4. 创建隔离 worktree:仅当 StageDirectory 不含 .git 时执行 git worktree add --detach <stage> <pin> 并 git submodule update --init --recursive;若目标存在但不是 worktree(Refusing to replace an existing non-worktree stage),脚本拒绝覆盖。

  5. 逐个补丁幂等应用:先用 git apply --reverse --check 判断是否已应用,未应用才 apply --check 再 apply。这样重复执行不会二次打补丁。

  6. 全量校验:调用 verify-chiaki-stage.py --stage <stage> --clean <checkout>。

  7. 构建并跑门槛测试:

    cmake -S services/remoteplay-probe -B <BuildDirectory> -G Ninja
      -DCMAKE_BUILD_TYPE=Release -DVEYRA_RP_BUILD_NATIVE=ON
      -DVEYRA_RP_CHIAKI_SOURCE_DIR=<stage> -DVEYRA_RP_CHIAKI_VERIFY_DIR=<clean>
      -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ...
    cmake --build <BuildDirectory> --config Release --target veyra_remoteplay_native_probe
    ctest --test-dir <BuildDirectory> -C Release -L native-core --output-on-failure --timeout 30
    

    最后一行输出的是明确的边界声明:Native initialization gate completed. PS5 connection, decoder and GPU are still NOT tested by this probe.

6.4 门槛测试的真实断言内容

services/remoteplay-probe/native_main.cpp 只做三件事:初始化 Chiaki 核心、校验 1080p60 预设字段、校验手柄 idle 状态:

auto init=veyra::remoteplay::initializeChiaki();
if(!init.ok){std::cerr<<init.operation<<" failed code="<<init.code<<'\n';return 1;}
ChiakiConnectVideoProfile p{};
chiaki_connect_video_profile_preset(&p,CHIAKI_VIDEO_RESOLUTION_PRESET_1080p,CHIAKI_VIDEO_FPS_PRESET_60);
ChiakiControllerState c{};chiaki_controller_state_set_idle(&c);
if(p.width!=1920||p.height!=1080||p.max_fps!=60||c.buttons!=0){...return 1;}
std::cout<<"REAL_CHIAKI_CORE_INITIALIZED metadata_api_available=1 session_started=0 video_callbacks_observed=0\n";

输出字符串出现 session_started=0 与 video_callbacks_observed=0 两个明示字段,正是为了堵住"把源码里的字符串当执行结果"的误读——这一点 docs/remoteplay/NATIVE_GATE.md 也有专门警告。

七、第二步:用 build.ps1 -RemotePlay 构建产品

7.1 参数表

scripts/build.ps1 的关键参数:

参数 必填 说明
-Root 是 仓库根,脚本先校验 CMakePresets.json 存在,否则退出码 2
-Preset 是 只能是 x64-debug 或 x64-release
-BuildDirectory 否 覆盖默认的 out\build\<Preset>
-RemotePlay 开关 置 VEYRA_ENABLE_REMOTEPLAY=ON 并注入串流五项路径
-ChiakiCheckout / -ChiakiStage / -RemotePlayPrefixPath / -ProtocPath / -PkgConfigPath 开关打开时必填 五者必须都真实存在
-FfmpegRoot 否 默认 C:\veyra-deps\installed\x64-windows
-Clean 开关 先清空构建目录

7.2 调用示例

powershell.exe -NoProfile -File scripts/build.ps1 `
  -Root . `
  -Preset x64-release `
  -RemotePlay `
  -ChiakiCheckout C:/veyra-deps/chiaki-source `
  -ChiakiStage out/remoteplay/chiaki-msvc-stage `
  -RemotePlayPrefixPath C:/veyra-deps/remoteplay-installed/x64-windows-static `
  -ProtocPath C:/veyra-deps/remoteplay-installed/x64-windows/tools/protobuf/protoc.exe `
  -PkgConfigPath C:/veyra-deps/vcpkg/downloads/tools/msys2/<hash>/mingw64/bin/pkg-config.exe `
  -FfmpegRoot C:/veyra-deps/ffmpeg-ps5-dav1d-installed

7.3 -RemotePlay 的五项前置校验

foreach ($path in @($ChiakiCheckout,$ChiakiStage,$RemotePlayPrefixPath,$ProtocPath,$PkgConfigPath)) {
    if (-not $path -or -not (Test-Path -LiteralPath $path)) { throw "Remote Play requires existing ChiakiCheckout, ChiakiStage, RemotePlayPrefixPath, ProtocPath and PkgConfigPath." }
}
$configureExtra += ' -DVEYRA_RP_CHIAKI_VERIFY_DIR="{0}" -DVEYRA_RP_CHIAKI_SOURCE_DIR="{1}" -DCMAKE_PREFIX_PATH="{2}" -DPROTOC="{3}" -DPKG_CONFIG_EXECUTABLE="{4}"'

注意这里同时传入了 stage(编译用) 与 clean checkout(校验用) 两个目录。这是"编译产物必须来自可验证树"的落地方式:CMake 在配置期会拿 clean 树重建期望内容,再与 stage 逐字节比较。

7.4 环境注入方式:临时批处理

脚本不假设当前 PowerShell 已有 MSVC 环境,而是生成一个临时 .cmd,在里面 call vcvars64.bat 后连续执行配置与构建,并用不同退出码区分失败阶段(scripts/build.ps1):

退出码 含义
2 找不到 CMakePresets.json
3 找不到 VS 2022 VC 工具或 cmake
4 vcvars64.bat 执行失败
5 CMake 配置失败
6 构建失败

构建成功后脚本还会把 avcodec-63.dll、avformat-63.dll、avutil-61.dll、swresample-7.dll、swscale-10.dll 复制到构建目录,并对 dav1d.dll 做"存在则复制、构建目录有而前缀没有则删除"的处理(scripts/build.ps1)。这与原文档"复用现有动态 FFmpeg DLL"的说法一致:串流组合包不静态吸收 FFmpeg。

八、CMake 侧:目标拆分与依赖选项矩阵

8.1 三个目标

cmake/VeyraRemotePlay.cmake 把串流拆成三层:

目标 内容 依赖
veyra_remoteplay_core Validation.cpp、AnnexB.cpp、Timeline.cpp、VideoIngress.cpp、SessionInbox.cpp 仅 Threads::Threads,C++20,无 Win32/FFmpeg/SDK
chiaki-lib 上游静态库,add_subdirectory(... EXCLUDE_FROM_ALL) 上游自身依赖
veyra_remoteplay_chiaki ChiakiBackend.cpp、ChiakiRegistration.cpp、ProfileStore.cpp、PsnAuth.cpp、Discovery.cpp chiaki-lib、crypt32 iphlpapi shell32 ole32 winhttp、json-c

"核心层不依赖 Win32/FFmpeg"不是风格偏好,而是让离线单测能在任意平台跑(见 services/remoteplay-probe/CMakeLists.txt 的 offline-core 标签)。

8.2 上游选项矩阵:全部强制

CMake 模块用 CACHE ... FORCE 逐一关闭上游无关组件,只保留库形态与 Opus:

set(CHIAKI_ENABLE_GUI OFF CACHE BOOL "" FORCE)
set(CHIAKI_ENABLE_CLI OFF CACHE BOOL "" FORCE)
set(CHIAKI_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
set(CHIAKI_ENABLE_FFMPEG_DECODER OFF CACHE STRING "" FORCE)
set(CHIAKI_ENABLE_PI_DECODER OFF CACHE STRING "" FORCE)
set(CHIAKI_ENABLE_SPEEX OFF CACHE STRING "" FORCE)
set(CHIAKI_ENABLE_RUDP OFF CACHE STRING "" FORCE)
set(CHIAKI_LIB_ENABLE_OPUS ON CACHE BOOL "" FORCE)
set(CHIAKI_LIB_ENABLE_MBEDTLS OFF CACHE BOOL "" FORCE)

关键陷阱写在紧随其后的注释里:

Disabling RUDP does NOT remove curl/json-c/libevent/miniupnpc in the pinned lib. Provision their REAL matching-toolchain dev libraries; never replace them with empty imported targets or remove FEC to force the native gate green.

也就是说 CHIAKI_ENABLE_RUDP=OFF 并不能减少依赖面,Opus、OpenSSL、json-c、libevent、miniupnpc、curl、nanopb、Jerasure/gf-complete 仍然需要真实可用的匹配工具链构建产物。用空 imported target 或删 FEC 源码"让门槛变绿"的做法被明确禁止。

8.3 MINIUPNP_STATICLIB:一个典型的静态链接坑

# vcpkg's static miniupnpc headers default to dllimport on Windows unless the
# caller opts into the static ABI. Without this definition the static library
# is found correctly but Chiaki emits unresolved __imp_* references.
target_compile_definitions(chiaki-lib PRIVATE MINIUPNP_STATICLIB)

现象是"库找得到、链接报 __imp_* 未解析",根因是头文件里的 dllimport 语义与静态库 ABI 不匹配。排查静态串流链接问题时,这一行往往是第一嫌疑。

8.4 产物接线

根 CMakeLists.txt 在开关打开时把串流源码与后端接进 veyra_sources:

if(VEYRA_ENABLE_REMOTEPLAY)
  set(VEYRA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
  include(cmake/VeyraRemotePlay.cmake)
  target_compile_definitions(veyra_sources PUBLIC VEYRA_ENABLE_REMOTEPLAY=1)
  target_sources(veyra_sources PRIVATE src/source/RemotePlaySource.cpp src/source/RemotePlaySessionSource.cpp)
  target_link_libraries(veyra_sources PUBLIC veyra_remoteplay_chiaki)
endif()

PUBLIC 的编译定义会传播到依赖 veyra_sources 的测试与 UI 目标,apps/veyra/ui/AppShell.cpp 中多处 #ifdef VEYRA_ENABLE_REMOTEPLAY 的界面分支由此生效。

8.5 串流专属测试与探针目标

CMakeLists.txt 在开关打开时追加一批只在序号打开时才存在的目标:

目标 用途
veyra_ps5_hw_image_tests PS5 硬件图像路径
veyra_ps5_quality_tests 串流画面质量
veyra 追加 apps/veyra/ui/RemotePlayPanel.cpp 与 src/remoteplay/ControllerInput.cpp,链接 SDL3::SDL3 Win32 前端的串流面板与手柄
veyra_qml_bridge 追加 ControllerInput.cpp,只链接 SDL3::SDL3-static 的库文件与 SDL3::Headers QML 前端复用同一手柄通道
veyra_remoteplay_boundary_tests 产品边界(tests/remoteplay/ProductBoundaryTests.cpp)
veyra_remoteplay_profile_tests DPAPI 凭据存储
veyra_remoteplay_probe 无 GUI 的串流探针,用已保存 profile 比较编码/码率变体(CMakeLists.txt)

关于 QML 分支为何只链 SDL 库文件而不链 SDL3::SDL3,源码里给了很具体的理由:SDL3::SDL3 的系统库列表会把 veyra_qml_ui 的链接行推过 CMake 的响应文件阈值,而 link.exe 以 ANSI 代码页读取响应文件,会让 E:/项目 这类 UTF-8 路径失效。这类"构建几何"问题在静态依赖较多的工程中很常见,值得复现时留意。

九、验证分级:哪些通过了,哪些没有

串流相关验证被刻意分成四级,避免把弱证据当强结论。

第 0 级 — 离线核心单测(无 Chiaki、无 Win32、无 FFmpeg):

powershell.exe -NoProfile -File scripts/remoteplay/test-core.ps1 -Configuration Debug

该脚本以 -DVEYRA_RP_BUILD_NATIVE=OFF 配置,用 file(STRINGS ... REGEX "^TEST\\(...") 从测试源码生成逐用例 CTest 条目,标签为 offline-core,超时 30 秒。脚本末尾同样打印边界:This does NOT test the Windows player, Chiaki native linkage, PS5 or GPU.

第 1 级 — 原生初始化门槛:第六节的 -L native-core,标签为 native-core;not-ps5(services/remoteplay-probe/NativeChiaki.cmake)。通过只说明"真实 chiaki-lib 能编译、链接、初始化"。

第 2 级 — 适配层源码回归:可选目标 veyra_remoteplay_source_tests,需要 -DVEYRA_RP_FFMPEG_ROOT 指向既有 FFmpeg 安装,链接 avcodec/avutil/avformat。它验证 RemotePlaySource 的解码消费逻辑,但仍不等价于真机串流。

第 3 级 — 产品交付门槛:1.4.1 的实际验证记录见 docs/history/releases/RELEASE_1.4.1_EXECUTION.md,包括 scripts/gates/delivery.ps1 的 26 项检查 PASS、八项定向用例 PASS,以及从最终 ZIP 解压后运行的七项便携冒烟用例。即使这样,文档也只声称"RTX5070 上的兼容性测试不替代用户在 RTX30/40 上的实测",并未把结果外推成通用兼容矩阵。

十、运行时契约:比构建更容易踩的坑

原文档只讲构建,但"构建成功"与"串流可用"之间隔着一层严格的线程与生命周期契约。这部分在 docs/remoteplay/NATIVE_GATE.md 有明确约定,并在 include/veyra/remoteplay/ChiakiBackend.h 中以注释形式固化进接口。

1) 单线程 owner 模型

// All methods, including snapshot(), must be called from one session-owner thread,
// NEVER from a Chiaki callback. This object is not a renderer or a decoder.

网络回调只做两件事:拷贝输入、投递到 SessionInbox。回调里不得调用增强图、Present、UI、磁盘日志或 join。

2) 每次会话先取 token

SessionInbox.begin(commonOrigin) 产生 token 后,才能 ChiakiBackend.start(request, token)。请求里应包含明确的 host、720/1080p 30/60 SDR 预设和 8/16/16 字节配对凭据——不得把凭据塞进路径、环境变量或命令行。

3) 参数校验的硬边界

src/remoteplay/Validation.cpp 直接定义了这支集成档位的可接受范围:

if (!((width==1280 && height==720) || (width==1920 && height==1080)))
    return "Only 720p/1080p are supported by this integration profile";
if (fps!=30 && fps!=60) return "Frame rate must be 30 or 60";
if (bitrateKbps<1000 || bitrateKbps>100000) return "Bitrate must be 1000..100000 kbit/s";

这意味着"4K 串流"在这一版不属于集成档位,配置更高分辨率会在校验阶段被拒绝,而不是在解码器里出错。

4) 凭据处理的默认收敛

PairingCredentials 删除拷贝构造、只在移动时存在,且头文件注释明确"不做序列化/ostream 支持,绝不把注册/会话密钥放进命令行、日志、fixture 或源码管理"。NativeSnapshot 里的 serverTargetBitrate 标注为 Raw upstream field; diagnostic opt-in only——上游原始字段默认不进入日志。

5) 终止顺序不可省略

inbox.invalidate → backend.stop(含 stop/join/fini)→ 确认清理成功 → inbox.finishStop。新会话前必须先真正关闭旧 backend,不能只重置 inbox 来假装线程已停。stop() 的注释补充说明它不会向 PS5 发送 goto_bed。

6) IDR 与丢包恢复

takeIdrRequest() 需由 owner 周期性取走并调用 backend.requestIdr();保守丢包处理会等待 IDR,而 H.265 的 CRA 不会被当作 IDR,需要真机确认编码习惯与恢复行为。docs/remoteplay/NATIVE_GATE.md 也提醒 pairLocalPs5 的超时是"请求取消时点",不是已证实的底层 join 时限,UI 不应同步等待。

十一、失败排查对照表

以下条目全部来自仓库中可验证的 FATAL_ERROR、throw 或校验分支。

现象/消息 根因 处理 出处
VEYRA_RP_BUILD_NATIVE needs the pinned chiaki-ng source tree VEYRA_RP_CHIAKI_SOURCE_DIR 下找不到 lib/include/chiaki/session.h 检查 stage 是否生成、路径是否指向 stage 而非 clean 树 cmake/VeyraRemotePlay.cmake
Chiaki source verification failed: ... stage 内容与"钉子 + 两个补丁"重建结果不一致 重新创建 worktree 并重打补丁;不要手工改 stage cmake/VeyraRemotePlay.cmake
Upstream chiaki-lib target was not created 上游选项被外部 cache 覆盖或 add_subdirectory 未执行 清理构建目录后重新配置 cmake/VeyraRemotePlay.cmake
链接期 __imp_* 未解析 静态 miniupnpc 未定义 MINIUPNP_STATICLIB 确认该 target_compile_definitions 未被移除 cmake/VeyraRemotePlay.cmake
Could not find protoc 未传 -ProtocPath 传真实 protoc.exe 绝对路径 scripts/build.ps1
Chiaki source pin mismatch 检出 HEAD 不是固定提交 git checkout --detach 0e1695... scripts/remoteplay/build-native.ps1
Chiaki source checkout must be clean 检出树有已跟踪文件改动 还原改动后再构建 同上 #L27-L28
Refusing to replace an existing non-worktree stage stage 路径已存在但不是 git worktree 换一个空的 -StageDirectory scripts/remoteplay/build-native.ps1
Chiaki index must be unchanged / Chiaki submodule absent or pin mismatch stage 暂存区被改动,或子模块未初始化 重建 worktree 并 submodule update --init --recursive scripts/remoteplay/verify-chiaki-stage.py
Remote Play requires existing ChiakiCheckout, ChiakiStage, RemotePlayPrefixPath, ProtocPath and PkgConfigPath. -RemotePlay 少传路径 五个路径全部给全 scripts/build.ps1
Ambiguous source: <dep>(打包时) vcpkg buildtrees 下同名依赖有多个 *.clean 清理旧 buildtree 或显式确认版本 scripts/package-remoteplay-source.py
Port mismatch(打包时) port 配方与已安装 SPDX 记录的 SHA256 不一致 重新安装该依赖,勿手工替换 port 文件 scripts/package-remoteplay-source.py
Output exists(打包时) 目标 ZIP 已存在 换输出路径;脚本以 'x' 模式打开,绝不覆盖 scripts/package-remoteplay-source.py

十二、复现检查清单

把原文档的三句话扩展成一份可执行清单:

  1. 确认开关语义:目标产物是否真的需要 PS5 串流?不需要就保持 VEYRA_ENABLE_REMOTEPLAY=OFF,默认构建不会编入任何 Chiaki 代码。
  2. 准备 clean 检出:固定提交 0e16950165f06e5c3291537c2eeba6e852be7120,递归子模块齐全,工作区干净。
  3. 跑第一步:scripts/remoteplay/build-native.ps1 带齐 -PrefixPath -ProtocPath -PkgConfigPath,看到 CHIAKI_STAGE_EXACT_PATCHES_VERIFIED 与 native-core 测试通过。
  4. 跑第二步:scripts/build.ps1 -Root . -Preset x64-release -RemotePlay,五个路径与第一步保持一致,注意 stage 与 clean 是两个不同目录。
  5. 核对产物依赖形态:串流用静态 Chiaki/SDL/依赖,FFmpeg 仍是外部 DLL;Windows 网络栈、WinHTTP、DPAPI、WASAPI、DirectX 与 GPU 驱动组件不打包。
  6. 交付对应源码:scripts/package-remoteplay-source.py --version <版本> --output <路径>,保留生成的 .zip.sha256,确认 source-manifest.json 与 excluded-development-binaries.json 存在;FFmpeg 源码另行打包。
  7. 核对许可证记录:licenses/remoteplay/ 全套 notices 与 SPDX 记录随包;组合程序的许可记录必须同时覆盖 AGPL-3.0-only + OpenSSL 例外与 Veyra 自身 GPLv3,不能只有后者。
  8. 明确证据等级:native-core 通过 ≠ PS5 可连;离线核心单测通过 ≠ 解码可用;RTX5070 上通过 ≠ RTX30/40 通用。写发布说明时应保留这些边界语句。

围绕本篇主题继续深入,可参考 scripts/remoteplay/dependency-lock.json 的依赖钉子、cmake/VeyraRemotePlay.cmake 的完整选项矩阵、docs/remoteplay/NATIVE_GATE.md 的原生门槛与协议桥契约,以及 scripts/remoteplay/verify-chiaki-stage.py 的校验实现。这些文件共同构成了 Veyra-NRVideo 串流组件"可复现、可审计、可合规再分发"的完整证据链。

登录后查看全文
Veyra-NRVideo