wiliwili 跨平台 B 站客户端架构指南:borealis 分层架构、异步安全宏与 MPV 播放管线深度解析

发布时间:2026/9/17 9:38:42
wiliwili 跨平台 B 站客户端架构指南:borealis 分层架构、异步安全宏与 MPV 播放管线深度解析
wiliwili 跨平台 B 站客户端架构指南:borealis 分层架构、异步安全宏与 MPV 播放管线深度解析【免费下载链接】wiliwili第三方B站客户端目前可以运行在PC全平台、PSVita、PS4 、Xbox 和 Nintendo Switch上项目地址: https://gitcode.com/GitHub_Trending/wi/wiliwiliwiliwili 是一款以 C17 编写的跨平台 Bilibili 客户端,可在 Nintendo Switch、PSVita、PS4 与 PC 全平台运行,UI 基于 nanovg 渲染、GLFW/SDL2 窗口化、mpv FFmpeg 播放。本文基于仓库内的代码库指南文档,结合核心源码逐层拆解其类 Android的 Activity/Fragment/Presenter 分层、防 use-after-free 的异步宏、WBI 签名 HTTP 层、RecyclingGrid 复用列表、Shader 着色器配置与多平台构建体系,帮助你在不破坏既有架构的前提下快速上手二次开发与主题定制。技术栈总览组件选型说明UI 渲染nanovg基于自定义 fork 的borealis框架(library/borealis/),所有控件继承brls::Box及其子类窗口化GLFW 或 SDL2通过-DUSE_SDL2ON切换视频播放mpv FFmpeg由MPVCore单例封装,支持 Framebuffer/软渲染/D3D11/deko3d/GXM 多种后端网络cpr nlohmann/json封装在bilibili::HTTP,全部异步语言标准C17见 CMakeLists.txt架构分层(类 Android)wiliwili 借鉴了 Android 的分层模型,各层职责与目录一一对应:层目录职责Activitywiliwili/include/activity/、wiliwili/source/activity/顶层全屏页面(主界面、播放器、搜索、设置等)Fragmentwiliwili/include/fragment/、wiliwili/source/fragment/Activity 内部组合的子页面(首页各 Tab、我的各列表、搜索分类等)Presenterwiliwili/include/presenter/异步取数逻辑;Fragment 继承 Presenter 获得异步安全保护Viewwiliwili/include/view/可复用自定义 UI 组件(MPVCore、RecyclingGrid、VideoView 等)APIwiliwili/include/api/bilibili/Bilibili REST 接口封装,基于 cpr nlohmann/jsonUtilswiliwili/include/utils/Intent(导航)、ProgramConfig(设置)、ImageHelper、EventHelper 等从源码结构看,这种分层的落地方式是:Activity 通过Intent打开,内部组装若干 Fragment;每个需要网络数据的 Fragment 同时继承对应 Presenter 完成异步取数并驱动 UI 刷新;页面骨架则由 XML 布局文件声明式构建(见下文)。核心开发模式Presenter 与异步安全宏所有取数 Fragment 都继承 presenter.h 中的Presenter基类。异步网络回调可能晚于页面销毁返回,直接捕获this会导致 use-after-free。Presenter用一对堆分配的deletionToken/deletionTokenCounter指针解决该问题:宏捕获这两个堆地址而非this,即使对象析构,指针本身仍有效;析构函数会把*deletionToken置为true,回调端读到该标记即可提前返回。void requestSomething() { ASYNC_RETAIN // 捕获 token、tokenCounter(引用计数 1) bilibili::HTTP::getResultAsyncMyType(url, params, ASYNC_TOKEN { ASYNC_RELEASE // 视图已销毁时提前 return,并把引用计数 -1 brls::Threading::sync([this, result]() { // 主线程中安全更新 UI }); }); }从宏的源码实现看:ASYNC_RETAIN(见 presenter.h):首次使用时new出bool*与int*,随后引用计数 1;ASYNC_RELEASE(见 presenter.h):先快照release与counter,计数归零时才真正delete并清理成员;if (release) return;保证已销毁对象上的回调不再执行任何逻辑;ASYNC_TOKEN展开为this, token, tokenCounter三个 lambda 捕获项,其中this仅在ASYNC_RELEASE通过后才会被使用。另外,CHECK_AND_SET_REQUEST/UNSET_REQUEST宏(展开为requesting标志的置位/复位)用于防止同一页面重复发起在途请求。XML 驱动的 UI布局文件位于 resources/xml/,按activity/、fragment/、views/三个子目录组织;在构造函数中膨胀:this-inflateFromXMLRes(xml/fragment/home_recommends.xml);用 XML id 绑定子控件:BRLS_BIND(RecyclingGrid, recyclingGrid, home/recommends/recyclingGrid);自定义控件必须在使用前注册:brls::Application::registerXMLView(RecyclingGrid, RecyclingGrid::create);,注册集中在Register::initCustomView(),由 main.cpp 在brls::Application::init()与createWindow()之后调用(实现位于 register_helper.cpp);已注册的自定义 XML 元素名:UI 组件:AutoTabFrame、RecyclingGrid、VideoView、VideoProfile、QRImage、SVGImage、TextBox、VideoProgressSlider、GalleryView、CustomButton、HintLabel应用视图:UserInfoView、UpUserSmall、VideoComment、ButtonClose、CheckBox、SelectorCell、AnimationImage、ShareBox、DynamicVideoCardView、DynamicArticleViewFragment:HomeTab、DynamicTab、MineTab、HomeRecommends、HomeHotsAll、HomeHotsHistory、HomeHotsWeekly、HomeHotsRank、HomeHots、HomeLive、HomeBangumi、HomeCinema、MineHistory、MineLater、MineCollection、MineBangumi、SearchTab、SearchOrder、SearchVideo、SearchCinema、SearchBangumi、SearchHots、SearchHistoryXML 中的 i18n key 语法为i18n/wiliwili/some/key;翻译文件位于 resources/i18n/,支持en-US、zh-Hans、zh-Hant、ja、ko、it、ja-RYU七种语言目录,各含wiliwili.json。导航:统一走 Intent,禁止直接 push所有页面跳转入口定义在 activity_helper.hpp 的Intent类中:Intent::openBV(BV1Da411Y7U4); // 按 BV id 打开视频 Intent::openSeasonByEpId(323434); // 番剧剧集 Intent::openLive(1942240); // 直播间 Intent::openSearch(keyword); Intent::openSetting(); Intent::openCollection(2511565362); // 收藏夹 Intent::openPgcFilter(/page/home/pgc/more?type2...); // 影片分类索引从头文件声明看,Intent还提供openAV(AV id cid 进度)、openSeasonBySeasonId、openTVSearch、openInbox、openGallery(图片浏览)、openDLNA、openActivity(动态)等完整入口,覆盖了仓库中全部 Activity 的启动路径。HTTP API 层全部调用通过 http.hpp 中的bilibili::HTTP异步完成:// 标准 GET,解析 {code:0,data:{...}} HTTP::getResultAsyncMyResult(Api::SomeEndpoint, params, callback, error); // WBI 签名端点(2023 年后大部分 web-interface API 都需要): HTTP::getResultWithWbiAsyncMyResult(Api::SomeEndpoint, params, callback, error); // 需要 App 签名的端点(传 needSigntrue): HTTP::getResultAsyncMyResult(url, params, callback, error, /*needSign*/true); // 带类型响应的 POST: HTTP::postResultAsyncMyResult(url, params, payload, callback, error);源码层面的关键事实:请求默认值(http.hpp):User-Agent: wiliwili、Referer: https://www.bilibili.com/client、Origin: https://www.bilibili.com、超时 10000ms;代理与 TLS 校验可通过SettingItem::HTTP_PROXY*/SettingItem::TLS_VERIFY配置;parseJson 回退逻辑(见 http.hpp):当code 0时优先取data(对象或数组),否则回退取result(对象),都找不到则回调Cannot find data错误;WBI 签名:getResultWithWbiAsync先经 wbi.hpp 的updateWbiKeys从Api::Nav拉取img_keysub_key计算mixin_key,再由encWbi为参数追加wts与w_rid;密钥缓存 1 小时;App 签名:signParameters使用内置的BILIBILI_APP_KEY/BILIBILI_APP_SECRET/BILIBILI_BUILD(见 http.hpp);会话复用:所有请求共享一个CurlSharedObject(curl_share只共享 DNS 缓存并配递归锁),减少重复 DNS 解析。API URL 常量集中在 api.h;各接口的 JSON 结果结构体按接口拆分存放于 result/ 目录(如video_detail_result.h、search_result.h、home_result.h等)。RecyclingGrid(自定义复用列表)实现RecyclingGridDataSource后调用recyclingGrid-setDataSource(...)即可接入,组件声明见 recycling_grid.hpp。以 player_activity.cpp 为典型示例:class DataSourceFoo : public RecyclingGridDataSource { RecyclingGridItem* cellForRow(RecyclingGrid* recycler, size_t index) override { auto* item (MyCell*)recycler-dequeueReusableCell(Cell); // 按类型复用 cell item-setData(list[index]); return item; } size_t getItemCount() override { return list.size(); } void onItemSelected(RecyclingGrid* recycler, size_t index) override { /* 处理点击 */ } void clearData() override { list.clear(); } };调用recyclingGrid-onNextPage([]{...})注册滚动到底部的回调,实现无限加载分页。图片加载:平台感知的尺寸后缀异步加载图片时,URL 必须追加平台感知后缀,避免全尺寸下载:ImageHelper::with(imageView)-load(url ImageHelper::h_ext); // 横版缩略图(PC 上为 672w_378h) ImageHelper::with(imageView)-load(url ImageHelper::v_ext); // 竖版缩略图(PC 上为 312w_420h) ImageHelper::with(imageView)-load(url ImageHelper::face_ext); // 头像(PC 上为 96w_96h)image_helper.hpp 中的IMAGE_EXT在定义USE_WEBP时解析为.webp,否则为.jpg;PSVita 使用更小规格(如横版256w_144h)。全局事件总线event_helper.hpp 通过EventHelper单例暴露三条总线:MPV_E— 播放器状态事件,枚举MpvEventEnum含MPV_LOADED、MPV_PAUSE、MPV_RESUME、MPV_IDLE、MPV_STOP、UPDATE_PROGRESS、END_OF_FILE、CACHE_SPEED_CHANGE、VIDEO_SPEED_CHANGE、VIDEO_VOLUME_CHANGE、RESET、RESTART等(枚举注释直接说明了各事件的订阅方与触发时机);APP_E— 应用级自定义事件(字符串 key void*指针);SEARCH_E— 搜索页专用事件(字符串 key void*指针)。用法:订阅MPV_E-subscribe([](MpvEventEnum e){ ... });,触发MPV_E-fire(MPV_PAUSE);。从源码注释可确认其典型协作链:mpv 状态变化 → 播放器组件刷新进度 UI → 弹幕组件按VIDEO_SPEED_CHANGE同步滚动速度 → DLNA 页面把VIDEO_VOLUME_CHANGE同步到投屏端。设置:ProgramConfig 单例ProgramConfig::instance()是必须在使用前init()的单例,且必须先于brls::Application::init()执行——main.cpp 中的顺序即为ProgramConfig::instance().init()→brls::Application::init()。所有设置项以SettingItem枚举形式定义在 config_helper.hpp,主要项:PLAYER_HWDEC/PLAYER_HWDEC_CUSTOM— 硬件解码方式;APP_RESOURCES— 当前启用的自定义主题 ID(切换后需重启生效);KEYMAP— 按键图标字体集:xbox(PC 默认)、ps、keyboard;HTTP_PROXY、HTTP_PROXY_STATUS、TLS_VERIFY— 网络设置;SHORTCUT_*— 可重绑定的键盘快捷键(修饰键字符串,如ctrl-r)。自定义主题系统主题目录位于{configDir}/theme/{id}/,可覆盖resources/下任意文件:主题目录必须包含resources_meta.json:{name:…,desc:…,version:…,author:…}启动时由ProgramConfig::loadCustomThemes()扫描发现,当前主题 ID 存于SettingItem::APP_RESOURCES;切换主题会调用DialogHelper::quitApp()——必须重启应用才能生效,因为主题资源在应用启动时一次性加载。自定义字体与图标将以下文件放入配置目录即可覆盖内置资源:文件名用途font.ttf主 UI 字体icon.ttf按钮图标字体emoji.ttfEmoji 字体danmaku.ttf弹幕覆盖层字体gamecontrollerdb.txtSDL 手柄映射库(仅桌面端)内置按键映射字体位于 resources/font/:keymap_xbox.ttf、keymap_ps.ttf、keymap_keyboard.ttf,与SettingItem::KEYMAP的三个取值一一对应。Anime4K / Shader 系统ShaderHelper单例(声明见 shader_helper.hpp)管理从{configDir}/shader.json加载的着色器配置:{ profiles: [ { name: Anime4K Mode A, shaders: [/path/to/Anime4K_Clamp_Highlights.glsl], settings: [[set, scaler, ewa_lanczossharp]] } ], animeList: [{ anime: 28223043, profile: Anime4K Mode A }] }settings条目支持三种指令:[set,key,val]、[change-list,key,op,val]、[run,cmd];从 shader_helper.hpp 的from_json解析逻辑看,若某条目未指明操作指令,会默认补上set,因此[scaler,ewa_lanczossharp]这类简写也可用;应用/清除:ShaderHelper::instance().setShader(index)/clearShader();animeList用于把某个番剧 season id 绑定到指定 profile(按源码注释,当前为打开后对全体视频生效、重启需重新开启的简单模式,直播不受影响)。MPV 渲染模式模式触发说明Framebuffer(默认)MPV_USE_FB(自动)要求 GL 3.2 / GLES 2.0;性能最佳无 Framebuffer-DMPV_NO_FBONmpv 全屏绘制、UI 叠加其上;用于 PS4、PSV-GL、GL 2.0软渲染-DMPV_SW_RENDERON纯 CPU;面向 UWP/D3D12 移植deko3dBOREALIS_USE_DEKO3DSwitch 原生路径,可 4K60 硬解GXMBOREALIS_USE_GXMPSVita 原生D3D11BOREALIS_USE_D3D11Windows Native/UWP默认硬解策略:Switch/GXM →auto,PSV-GL →vita-copy,PS4 →no,桌面端 →auto-safe。播放器封装见 mpv_core.hpp。视频清晰度编码Code清晰度备注1278K1204K1161080P60非 PSV 平台默认801080P64720PPSV GXM 默认32480PPSV OpenGL 默认16360P未登录时默认配置目录位置平台路径Nintendo Switch/config/wiliwili/PS4/data/wiliwili/PSVitaux0:/data/wiliwili/macOS(Release)~/Library/Application Support/wiliwili/Linux(Release)$XDG_CONFIG_HOME/wiliwili/或~/.config/wiliwili/Windows(Release)%LOCALAPPDATA%\xfangfang\wiliwili\任意平台(Debug 构建)./config/wiliwili/(二进制同目录)主配置文件为{configDir}/wiliwili_config.json,承载 cookie、refreshToken、全部SettingItem值、GA client id 与搜索历史。构建命令桌面端(macOS)brew install mpv webp cmake -B build -DPLATFORM_DESKTOPON make -C build wiliwili -j$(sysctl -n hw.ncpu) # 生成 macOS 应用包: make -C build wiliwili.app桌面端(Linux/Ubuntu)sudo apt install libssl-dev libmpv-dev libwebp-dev cmake -B build -DPLATFORM_DESKTOPON make -C build wiliwili -j$(nproc) # 系统级安装(带 .desktop 条目): cmake -B build -DPLATFORM_DESKTOPON -DINSTALLON -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr make -C build wiliwili -j$(nproc) sudo make -C build installLinux 安装所用的桌面条目与图标模板位于 scripts/linux/(含cn.xfangfang.wiliwili.desktop、appdata.xml与各级图标)。Nintendo Switch(推荐 Docker)docker run --rm -v $(pwd):/data devkitpro/devkita64:20251117 bash -c /data/scripts/build_switch.sh # deko3d 变体(4K60 硬解):见 scripts/build_switch_deko3d.shSwitch 需要自定义编译的 ffmpeg/mpv(devkitpro 默认包不支持网络流),可安装项目 Release 页提供的switch-ffmpeg与switch-libmpv包后:cmake -B cmake-build-switch -DPLATFORM_SWITCHON make -C cmake-build-switch wiliwili.nro -j$(nproc)相关交叉编译的 PKGBUILD 与补丁集中在 scripts/switch/ 与 scripts/switch-forwarder/。PSVita / PS4(Docker)# PSVita GXM(推荐): docker run --rm -v $(pwd):/src/ xfangfang/wiliwili_psv_builder:latest-gxm \ cmake -B cmake-build-psv -G Ninja -DPLATFORM_PSVON -DUSE_GXMON \ -DUSE_SYSTEM_CURLON -DUSE_VITA_SHARKOFF -DCMAKE_BUILD_TYPERelease \ cmake --build cmake-build-psv # PS4: docker run --rm -v $(pwd):/src/ xfangfang/wiliwili_ps4_builder:latest \ cmake -B cmake-build-ps4 -DPLATFORM_PS4ON -DMPV_NO_FBON \ -DUSE_SYSTEM_CPRON make -C cmake-build-ps4 -j$(nproc)PSVita 各依赖的 VITABUILD 位于 scripts/psv/,PS4 的 PKGBUILD 位于 scripts/ps4/。常用 CMake 开关开关作用-DUSE_SDL2ON使用 SDL2 替代 GLFW-DMPV_NO_FBON无 framebuffer 模式(PS4、PSV GL、GL 2.0)-DMPV_SW_RENDERONCPU 软渲染-DMPV_BUNDLE_DLLON将 mpv.dll 打包进 exe(Windows -DUSE_LIBROMFSON)-DDISABLE_OPENCCON跳过简繁转换库-DDISABLE_WEBPON跳过 WebP 支持-DINSTALLONLinux 系统级安装(含桌面条目)-DDEBUG_SANITIZERON启用 ASan/UBSan(仅 Debug)-DUSE_LIBROMFSON将资源内嵌进二进制-DBUILTIN_NSPON内嵌 NSP forwarder(仅 Switch)-DAPP_PLATFORM_CUSTOM_LIBSON手动管理依赖:APP_PLATFORM_INCLUDEAPP_PLATFORM_LINK_OPTION调试技巧命令行参数(在 main.cpp 中解析):-d输出 debug 级日志、-v开启 borealis 可视化调试覆盖层、-t把 mpv 输出转发到终端、-o file将日志写入文件;想直接测试某个页面而跳过 UI 导航,可取消 main.cpp 中相应Intent::open*()行的注释——那里已预留大量测试 BV id(弹幕防遮挡、高级弹幕、4K HDR、8K、多 P、多字幕、flv 拼接、Switch 端 FFmpeg/MPV 已知问题复现等);初始化顺序约束:ProgramConfig::instance().init()必须先于brls::Application::init(),它会加载 cookie 与全部设置;应用内网络诊断:设置 → 工具 → 网络诊断(实现位于 setting_network.cpp),可检查 API 连通性、系统/服务器时间差、WiFi 状态、IP 与 DNS;Switch 上若启动后黑屏,从 SD 卡删除/config/wiliwili/后重试。平台宏宏含义__SWITCH__Nintendo Switch__PSV__PlayStation VitaPS4PlayStation 4BOREALIS_USE_OPENGL/BOREALIS_USE_DEKO3D/BOREALIS_USE_D3D11/BOREALIS_USE_GXM渲染后端MPV_USE_FB/MPV_NO_FBFramebuffer 模式(由渲染后端自动推导)USE_WEBP启用 WebP 解码关键文件速查文件说明wiliwili/source/main.cpp入口;解析参数、注册视图、启动 Activitywiliwili/include/utils/activity_helper.hppIntent,全部导航入口wiliwili/include/utils/config_helper.hppProgramConfig单例、SettingItem枚举wiliwili/include/utils/event_helper.hpp全局事件总线MPV_E/APP_E/SEARCH_Ewiliwili/include/presenter/presenter.hASYNC_RETAIN/ASYNC_RELEASE宏wiliwili/include/api/bilibili/util/http.hppHTTP::getResultAsync、getResultWithWbiAsyncwiliwili/include/api/bilibili/util/wbi.hppWBI 签名实现wiliwili/include/api/bilibili/api.h全部 Bilibili API URL 常量wiliwili/include/view/recycling_grid.hpp复用列表组件wiliwili/include/utils/image_helper.hpp异步图片加载 平台尺寸 URL 后缀wiliwili/include/utils/shader_helper.hppAnime4K / mpv 着色器配置管理wiliwili/include/view/mpv_core.hppMPV 单例、播放控制、渲染后端wiliwili/include/view/danmaku_core.hpp弹幕(弹评论)覆盖层渲染wiliwili/include/api/live/danmaku_live.hpp基于 mongoose 的直播间 WebSocket 弹幕resources/xml/全部 UI 布局文件resources/i18n/七语言翻译文件scripts/各平台构建脚本,README.md覆盖 Switch 自定义 ffmpeg/mpv 说明【免费下载链接】wiliwili第三方B站客户端目前可以运行在PC全平台、PSVita、PS4 、Xbox 和 Nintendo Switch上项目地址: https://gitcode.com/GitHub_Trending/wi/wiliwili创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考