Veyra-NRVideo 1.4.1 Remote Play 串流构建与对应源码交付指南
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.
这句话有两个必须分开理解的事实:
-
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 静态库和对应测试目标才会进入构建图。 -
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 + 文件名) | 打包后计算 |
打包脚本还有两条"证据强度"设计值得注意:
- port 配方与 SPDX 交叉校验:读取已安装 prefix 下的
share/<dep>/vcpkg.spdx.json,把其中SPDXRef-port-file-*的期望 SHA256 与实际vcpkg/ports/<dep>/<fileName>比对,不一致立即抛错。这保证归档里的 port 配方确实是构建时使用的版本。 - 依赖源码唯一性:
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 脚本内部到底做了七件事
-
解析并校验仓库根与检出目录,把
-Root与-ChiakiCheckout都转成绝对路径。 -
钉住 HEAD:
git rev-parse HEAD必须等于0e1695...,否则抛Chiaki source pin mismatch。 -
要求干净:
git status --porcelain --untracked-files=no必须为空。 -
创建隔离 worktree:仅当
StageDirectory不含.git时执行git worktree add --detach <stage> <pin>并git submodule update --init --recursive;若目标存在但不是 worktree(Refusing to replace an existing non-worktree stage),脚本拒绝覆盖。 -
逐个补丁幂等应用:先用
git apply --reverse --check判断是否已应用,未应用才apply --check再apply。这样重复执行不会二次打补丁。 -
全量校验:调用
verify-chiaki-stage.py --stage <stage> --clean <checkout>。 -
构建并跑门槛测试:
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 |
十二、复现检查清单
把原文档的三句话扩展成一份可执行清单:
- 确认开关语义:目标产物是否真的需要 PS5 串流?不需要就保持
VEYRA_ENABLE_REMOTEPLAY=OFF,默认构建不会编入任何 Chiaki 代码。 - 准备 clean 检出:固定提交
0e16950165f06e5c3291537c2eeba6e852be7120,递归子模块齐全,工作区干净。 - 跑第一步:
scripts/remoteplay/build-native.ps1带齐-PrefixPath -ProtocPath -PkgConfigPath,看到CHIAKI_STAGE_EXACT_PATCHES_VERIFIED与native-core测试通过。 - 跑第二步:
scripts/build.ps1 -Root . -Preset x64-release -RemotePlay,五个路径与第一步保持一致,注意 stage 与 clean 是两个不同目录。 - 核对产物依赖形态:串流用静态 Chiaki/SDL/依赖,FFmpeg 仍是外部 DLL;Windows 网络栈、WinHTTP、DPAPI、WASAPI、DirectX 与 GPU 驱动组件不打包。
- 交付对应源码:
scripts/package-remoteplay-source.py --version <版本> --output <路径>,保留生成的.zip.sha256,确认source-manifest.json与excluded-development-binaries.json存在;FFmpeg 源码另行打包。 - 核对许可证记录:
licenses/remoteplay/全套 notices 与 SPDX 记录随包;组合程序的许可记录必须同时覆盖 AGPL-3.0-only + OpenSSL 例外与 Veyra 自身 GPLv3,不能只有后者。 - 明确证据等级:
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 串流组件"可复现、可审计、可合规再分发"的完整证据链。