ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Apollo 迁移指南:从 GameStream 到 UPnP 公网串流

Apollo 迁移指南:从 GameStream 到 UPnP 公网串流

Apollo 迁移指南:从 GameStream 到 UPnP 公网串流

【免费下载链接】ApolloSunshine fork - The easiest way to stream with the native resolution of your client device项目地址: https://gitcode.com/gh_mirrors/apollo18/Apollo

NVIDIA 在 2023 年 2 月终止了面向 NVIDIA Games 客户端的 GameStream 服务,而 Apollo(一个以"客户端设备原生分辨率串流"为目标的 Sunshine fork)的性能已达到或超过 GameStream,客户端仍可直接使用 Moonlight,无需更换。本文讲清两件事:如何用 GSMS 工具把原有游戏条目搬进apps.json,以及 Apollo 内置的 UPnP 公网串流机制、配置项和常见故障的排查思路。

结论先行:什么情况值得直接迁移

判断很简单:只要你的客户端是 Moonlight,迁移就是"换服务端、不换客户端",成本最低。

  • 串流质量:官方文档明确 Sunshine 性能已达到或超过 GameStream(见 docs/gamestream_migration.md)
  • 公网串流:原来依赖 Moonlight Internet Hosting Tool 打洞的,迁移后可直接移除该工具,改用 Apollo 内置的 UPnP 端口映射
  • 需要接受的差异:
能力GameStreamApollo
自动扫描并列出已安装游戏支持不支持,依赖apps.json条目或 GSMS 迁移结果
自动调整游戏内设置优化串流支持不支持,需用prep-cmd等手段自行编排
互联网串流依赖 NVIDIA 云服务本地 UPnP 端口映射,无需额外服务

如果游戏库很小,手工在apps.json里写几条就够用;条目多、怕出错,再上 GSMS 自动迁移。

GSMS:把游戏条目一次性搬进 apps.json

GSMS(GameStream Migration Service)是官方为迁移场景提供的自动化工具。它能把你 GameStream 里的自定义应用和自动检测到的游戏一起迁走,具体落点是:

  • 每个条目的工作目录、启动命令写入apps.json
  • 封面图(box-art)复制到你指定的目录

apps.json默认与主配置文件同目录(Linux/macOS 为~/.config/sunshine,Windows 为%ProgramFiles%\Sunshine\config,Docker 为/config),首次启动时由内置模板复制生成(src/config.cpp 中APPS_JSON_PATH的拷贝逻辑)。文件默认模板长这样:

// src_assets/linux/assets/apps.json { "env": { "PATH": "$(PATH):$(HOME)/.local/bin" }, "apps": [ { "name": "Low Res Desktop", "image-path": "desktop.png", "allow-client-commands": false, "prep-cmd": [ { "do": "xrandr --output HDMI-1 --mode 1920x1080", "undo": "xrandr --output HDMI-1 --mode 1920x1200" } ] } ] }

顶层env定义所有应用共享的环境变量,$(PATH)、$(HOME)这类占位符在运行时展开;apps数组里每个条目就是一个可串流启动的应用。解析逻辑在 src/process.cpp,常用字段如下:

字段类型默认值含义
namestring必填显示在客户端的名称
cmdstring空启动命令
working-dirstring空工作目录(Windows 上会自动去引号并补反斜杠)
image-pathstring空封面图路径,须为 PNG
elevatedboolfalse是否提升权限运行
auto-detachbooltrue自动分离进程
wait-allbooltrue等待所有子进程退出
exit-timeoutint5退出判定超时(秒)
prep-cmdarray空启动前/退出后执行的命令,每项含do、undo、elevated
detachedarray空独立于主进程启动的附加命令
virtual-displayboolfalse是否使用虚拟显示器
allow-client-commandsbooltrue是否放行 Alt+Tab 等客户端命令
uuidstring必填应用唯一标识,Web UI 增删改依赖它

两个实用细节:

  • 全局准备命令global_prep_cmd会在所有应用启动前执行,任一条失败就中止启动(docs/configuration.md 中的示例:用nircmd.exe临时切换分辨率,undo再切回)
  • Web UI 提供GET /api/apps和POST /api/apps两个 REST 接口(src/confighttp.cpp),新增或编辑应用后调用proc::refresh热刷新,不用重启 Sunshine。迁移完成后建议在这个页面上逐条核对cmd、working-dir、image-path是否指向正确路径

UPnP 公网串流的内部机制

想从外网串流,前提只有一个:路由器支持 UPnP。开启方式是 Web UI 打开 UPnP 选项,或在配置文件中写:

# docs/configuration.md(Network 一节) upnp = enabled

配置解析在 src/config.cpp:读到upnp = enabled后翻转flag::UPNP标志位;src/upnp.cpp 的upnp::start()发现该标志未置位就什么都不启动,否则创建一个后台映射线程。

线程启动时先组装端口映射清单,基址是port(默认 47989),实际映射的 6 个端口为:

端口协议路由表中的描述
48010TCPSunshine - RTSP
47998UDPSunshine - Video
48000UDPSunshine - Audio
47999UDPSunshine - Control
47989TCPSunshine - Client HTTP
47984TCPSunshine - Client HTTPS

只有当origin_web_ui_allowed设为wan时,才会额外映射 47990(Web UI 端口)——默认lan下管理端口不对外开放,这是安全设计,不是 bug。

映射线程的工作节奏(src/upnp.h中两个常量):

  • REFRESH_INTERVAL = 120s:每 120 秒重新发现 IGD(Internet Gateway Device,即支持 UPnP 的路由器)并逐端口核对映射。注释里写明原因:路由器重启、WAN IP 变化等都会让映射丢失,必须周期刷新
  • PORT_MAPPING_LIFETIME = 3600s:映射租约 1 小时,到期前被刷新逻辑续上

每个端口的处理顺序:先用UPNP_GetSpecificPortMappingEntry查是否已映射;目标 IP 一致就直接跳过;被别的设备占用则记UPnP conflict detected并尝试删除重建;如果路由器返回异常错误(非 606 Unauthorized),会判定为老旧的 IGDv1 设备,退化为 0 秒租约的永久映射来兼容。另外,若address_family = both且 IGD 支持 IPv6 防火墙控制,会额外为每个 LAN 端口打 IPv6 pinhole(IPv6 下没有 WAN→LAN 端口映射概念)。Sunshine 退出时调用unmap_all_upnp_ports清理全部映射。

相关配置项速查:

配置项默认值说明
upnpdisabled是否启用内置端口映射
port47989端口族基址,改动后上表所有端口整体偏移
address_familyipv4ipv4仅 IPv4;both为 IPv4+IPv6
origin_web_ui_allowedlanpc/lan/wan三级,决定 Web UI 是否对外
external_ip自动检测手动指定公网 IP,留空则自动

⚠️ 一条官方警告:Moonlight Internet Hosting Tool 低于 v5.6 的版本与 Sunshine 同时运行,会导致 UPnP 端口转发变得不可靠。迁移时直接卸载它,或至少升级到 v5.6 及以上。

连不上时,按这个顺序排查

外网串流失败大多是"映射没生效"或"映射被占",日志里都有对应线索:

  1. 发现不到 IGD:日志出现Couldn't discover any IPv4 UPNP devices或No IGD device found,说明路由器没开 UPnP(或根本不支持)。到路由器后台开启 UPnP/远程端口映射后,等 120 秒内线程自动重试即可
  2. 映射存在但指向别处:日志出现UPnP conflict detected with: <IP>,表示端口被局域网内其他设备占用。Apollo 会尝试删除对方映射后重建;部分路由器出于安全禁止未认证客户端删除他人映射,此时需手动到路由器后台清理旧规则
  3. 路由器重启后短暂连不上:属正常现象。映射丢失后线程会在下一轮(最多 120 秒)重新发现 IGD 并补建映射,同时打印Completed UPnP port mappings提示
  4. 改了port却忘了客户端:port一改,上表 6 个端口全部偏移,Moonlight 侧需要按新端口重连;手动在路由器上转发端口的用户要同步更新规则
  5. 想从外网管 Web UI 却连不上:确认origin_web_ui_allowed已设为wan,否则 47990 端口根本不会进入映射清单
  6. 公网 IP 探测不对:多出口/NAT 环境下自动检测可能失败,用external_ip手动指定

日志级别调到 debug 可以看到每一步:查映射、建 pinhole、失败回退永久映射等细节(Successfully mapped TCP 47989这类输出)都在这条线程里。

什么时候不适合这套方案

  • 路由器不支持 UPnP(部分运营商定制路由关闭了该功能):内置映射无从生效,只能手动配置路由器端口转发,本文机制不覆盖
  • 游戏库更新频繁:Apollo 不会自动扫描新装游戏,新游戏不会自己出现在串流列表里,需要重新跑一次 GSMS 或在 Web UI 手工添加
  • 希望服务端替你调优游戏:GameStream 会自动改游戏内设置适配串流,Apollo 不会干预游戏内部配置。替代做法是用prep-cmd/state-cmd在串流前后执行自己的命令(切分辨率、开低延迟模式等),把 GameStream 的"自动化"换成"脚本化"

收个尾:迁移本身是一次性动作(GSMS 跑一遍 + 核对apps.json),真正的长期成本在网络侧。UPnP 开关只影响 6 个固定端口的映射,日志每 120 秒一轮自检,出问题时先看映射状态再看 IP,基本能定位九成故障。

【免费下载链接】ApolloSunshine fork - The easiest way to stream with the native resolution of your client device项目地址: https://gitcode.com/gh_mirrors/apollo18/Apollo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表