news 2026/10/23 2:36:45

pure_live 虎牙双端短暂停顿审计:短租约取证、mpv 缓存预算与换源竞态修复路线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pure_live 虎牙双端短暂停顿审计:短租约取证、mpv 缓存预算与换源竞态修复路线
  • 音视频
  • 直播
  • 移动开发

【免费下载链接】pure_live

纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。

项目地址:https://gitcode.com/gh_mirrors/pur/pure_live
点击查看免费下载

本篇以 pure_live 仓库中 2026-09-05 的虎牙连续性审计文档为主体,完整还原“Android/Windows 双端短暂停顿”这一问题的取证过程:从匿名房间 660000 的 FLV/HLS 长读探测、mpv 原生缓存预算的语义纠错,到续签期间暂停竞态的代次冻结方案,并对照上游c5944dd5/543a3ded等提交厘清本地分叉与上游实现的实际差异。读完可以掌握:直播客户端如何为“连接短租约”建立证据边界、如何正确配置 media_kit/mpv 的缓存属性组合、以及换源(source refresh)事务中用播放意图代次防止过期异步结果覆盖用户操作。

冻结范围与归因分类

本轮排查在明确的基线冻结下进行,避免“边改边测”污染证据链:

  • 本地基线812553c2;冻结的 upstream/master 为db067f193bba47ea08a91cc0b4f5cda5f291f5be。本轮没有合并上游,也没有连接手机。
  • 用户确认 Android、Windows 均有短暂停顿,旧版本主要表现为黑屏;发生频率、声音是否中断和安装包对应内核尚未取得同场采样。
  • 短租约、Windows 提前切换及备用播放器路径可追溯至维护分支db7d0df33,不是本轮上游更新引入。
  • 缓存预算由维护分支3c15096e引入,但与锁定的 media_kit 原生默认属性组合不完整,分类为integration-conflict。
  • 换源等待期间缺少播放意图代次检查,属于维护分支恢复逻辑的fork-regression;它是独立的状态竞态,不当作所有停顿的同一原因。

归因上的关键纪律是:不把操作竞态等同于全部前台自然停顿。本文档与后续文档(如关闭与快速重进审计、原生令牌连续播放修复、恢复队列审计、缓存与内存趋势审计、事件合同审计)共同构成分层修复记录,各修复层独立归因、独立回滚。

外部接口证据:FLV 短连接与 HLS 签名失效(非实机播放验收)

审计先在 Flutter/播放器/自动换源之外,用匿名公开房间 660000 做独立探测,脱敏结果保存在本地local-artifacts/diagnostics/huya-continuity-20260905/。两个关键样本:

  1. 单连接 FLV:HTTP 200,首字节约 0.39 秒;读取 5,209,234 字节后,连接在约 120.949 秒后 EOF,客户端未主动重开。
  2. 同一个签名 HLS URL:播放列表序号持续递增,124.48 秒时仍为 HTTP 200;130.84 秒开始,播放列表本身返回 HTTP 403,后续三次请求仍为 403。

初步“只有 FLV 有短有效期、换 HLS 就消失”的假设被第二次分阶段探测否定。审计的处理方式是:撤销该假设对应的临时测试,保留现有 HLS 租约保护,不为让测试通过而改变正确行为。

这些样本支持的结论是有边界的:

  • 支持“当前获取方式下存在短期传输/签名有效性约束”;
  • 没有证明全部 CDN 的固定 SLA;
  • 没有证明登录可以解除约束;
  • 没有证明每次用户停顿恰好发生在该时点;
  • 在官方机制文档缺失时,不把 403 直接命名为某种账户验证。

后续在同条件长期对照(见原生令牌连续播放修复)中,网页模板路径与上游 WUP 原生令牌路径的 A/B 结果进一步收窄了结论:网页模板 + 匿名身份约 120.8 秒 EOF,而 WUP 原生凭据在 420 秒(测试上限)内连接正常,24,206,698 字节。这印证了本文“尚未定位取流根因”的早期状态,本文的缓存/竞态结论仍有效,但取流策略结论以该后续文档为准。

客户端链路与第一个错误状态

续签地址不等于连续媒体

问题一:Android 的 proactive 路径只保存_prefetchedSourceRefresh——即预取的地址。当旧连接结束,仍需重新打开媒体源、建连接、探测和解码。这可以缩短“获取地址”的等待,但没有消除媒体重开间隔。

问题二:Windows 的_tryWarmSwapSource等到候选画面出帧后交接,能减少黑屏;但旧播放器先 pause,随后还要设置候选音量、切换订阅并发布纹理变更。两段媒体没有时间戳对齐,也没有 Flutter 实际呈现提交确认,所以“候选已出帧”并不证明用户看到的画面无间隔。审计明确判定:现有日志里的 seamless 描述过度,应改为仅描述完成 handoff。

据此,文档直接否决了四类无证据修法:

  • 无证据缩短重试;
  • 直接删除租约保护;
  • 全平台启用双解码器;
  • 盲目扩大缓冲。

原生缓存预算未完整生效

审计发现应用当时只写入前向 32 MiB、后向 4 MiB、readahead 2 秒,而锁定的 media_kit 默认开启cache=yes和cache-on-disk=yes。按 mpv 官方手册的语义:

  • 开启磁盘缓存时,demuxer-max-bytes约束的是内存中的包元数据,而不是磁盘中的媒体数据,文件可持续追加;
  • 网络缓存启用时,较大的cache-secs优先于较小的demuxer-readahead-secs。

因此旧注释中的“36 MiB 压缩媒体内存预算、2 秒预读”并不是完整的有效配置。当时的修复设计是:显式启用网络缓存、关闭磁盘缓存,保留 32/4 MiB 字节预算,明确设置 6 秒网络缓存时间上限、2 秒基础预读。审计同时强调:6 秒是有界抖动余量,不是额外等待 6 秒才开播,也不是断流无缝续接的保证。主播放器与多画面共用同一属性入口,不修改录制管线、播放比例、音频模式和手动暂停语义。

对照当前主线源码,MediaKitLiveProperties 的直播调优表已演进为:forwardBytes = 96 MiB、backBytes = 8 MiB(低配 4 核以下机器降为 40/5 MiB)、readaheadSeconds = 8、cacheSeconds = 30、cache-pause-wait = 4,并显式写入cache: yes、cache-on-disk: no、demuxer-donate-buffer: no(防止已播放数据借用前向预留)。build()的注释还保留了一个真实教训:Twitch 复现中,按源写的force-seekable=no曾被这张全局表覆盖为yes——这正是把“全局属性”与“按源属性”拆成build()与sourceProperties()两个入口的原因:

  • sourceProperties() 按源决定http-proxy、demuxer-lavf-format、force-seekable、cache-pause:远端渐进式流(FLV 等)一律按直播对待,cache-pause只在点播(本机/回环输入)保留,否则会出现“先播两秒、停进缓冲、再续播”的观感;
  • 引擎跨源复用场景下,非 HLS 上游必须清空demuxer-lavf-format的强制值,否则下一条 FLV 会被按 HLS 解析;
  • media_kit_source_properties_test.dart 对上述每条规则都有回归用例,包括“平台声明优先于 URL 形状”(Twitch 把签名塞在路径里、无.m3u8后缀也需声明 hls)。

另外,player_preset.dart 中的预设属性(如lowLatency关闭缓存、stableNetwork把cache-secs提到 120 秒并开启重连)以“后写覆盖”的方式叠加在这张基础表之上,这也解释了为什么缓存预算必须是显式、完整、单入口的——任何一个写入者漏项都会让组合回落到引擎默认。

续签期间用户暂停后,旧异步结果仍可能提交

这是审计定位到的明确状态竞态:定时器触发换源时检查了播放意图,但 resolverawait完成后没有重新检查。用户在等待期间暂停,过期结果仍可创建并播放新候选,覆盖用户暂停。

修复设计:

  1. 在解析入口冻结 session 与 playbackIntentRevision;
  2. 异步结果返回及候选准备完成时检查代次、播放请求和临时挂起状态;
  3. 过期结果按“已消费事务”处理,避免继续走错误恢复重开;
  4. 保持正常主动续签、以及死亡连接同 URL 重开的原有行为不变。

当前主线在 playback_source_refresh.dart 中的canAdoptSourceRefresh()仍然执行同一族门禁:!disposed && sameRoom && !revisionMoved && result.hasSources——换源是一次往返请求,期间任何超播放的事务都会使过期结果被拒绝采用;player_controller.dart 中的 commit 则以commit.revision <= 已见revision做幂等去重。虎牙侧的续期决策还集中在 huya_transport_policy.dart 的selectRefreshedLine():按“身份而非旧快照中的位置”续线,优先保留原 CDN/原格式/原原生凭据家族,主播重开导致流名轮换时按 host+格式识别线路,只在单条原生线失败时才放宽到网页 FLV、再到 HLS。

上游实际修法与本地差异

用户在追加要求中只执行 fetch、未 merge/rebase/cherry-pick;此次审阅的最新上游固定为baab0eb4(2026-09-04),此前冻结基线仍用于本地来源追踪。

令牌获取与故障恢复是不同层

  • 上游c5944dd5(8 月 29 日,标题并未准确列出所有改动)将getPlayUrl改为先执行getCndTokenInfoEx,由 WUP 返回sFlvToken再构造地址;缓存两分钟并合并并发请求,当前上游 master 仍保留此路径。
  • 本地优先使用可用的房间网页模板,WUP 是补救路径;另有匿名观看身份、按协议选令牌、完整 fm 模板、lease 元数据、主动续签与 Windows 候选播放器。
  • 当前上游的getPlayUrl即便后缀是 m3u8,令牌获取仍统一读取sFlvToken,且缓存键只有 stream——这些与协议/身份一致性相关的差异应单独验证,不整文件覆盖本地。
  • 上游6d62f7ae的作者信息与标题源于本维护分支的一批改动,但其实际差异没有完整携入本地 Huya lease/Windows warm-swap 管线;同名提交也不等于实现一致。

本地源码印证了“按协议选令牌”的隔离:huya_site.dart 中 HLS 线路走getNativeCdnTokenInfoEx()取得的凭据只用于构造 HLS 地址,注释明确写着 “Never reuse its FLV token for HLS”;HuyaCdnTokenLease 对齐官方播放器的租约语义——wsTime获得 5 分钟有效期宽限、提前 30 秒请求刷新(refreshAt = invalidAt - 30s),并在存在服务端iExpireTime时取更早的失效时间。续期时间取“签名失效提前量、令牌租约刷新点、传输年龄”三者的最早值(见 getPlayUrlRefreshAt),网页/未知家族保留 100 秒刷新、125 秒失效的短传输租约保护(_transportRefreshAge/_transportInvalidAge)。

证据顺序同样重要:先前测到的 121 秒 EOF 和 131 秒 403 使用的是本地网页模板路径。在对上游 WUP 路径做同条件长期对照之前,不把结果推广成“虎牙所有获取方式的服务器固定限制”——这也是后续应优先补齐的证据,早于增加双播放器和更频繁重试。

IJK 黑屏修复的最终状态

  • 543a3ded(9 月 4 日)包含:没有视频尺寸时仍构建视图(16:9 临时占位);纹理 ID 判断从> 0改成>= 0;一度增加视图帧后自动启动逻辑;移除start-on-prepared=0。
  • 后续a6494321完整还原了该次 FijkAdapter 改动,恢复autoPlay:true。当前上游 master 保留视图/纹理修正,但不保留那套帧后自动启动状态机。
  • 本地的比例控制、来源代次、音频和 PiP 状态机有额外约束。借鉴视图挂载与合法纹理 ID 修正时,应加 IJK 独立回归;这并非 Windows media_kit/CDN 短暂停顿的直接解决方案。

Issue 证据边界

  • 808 号 issue 中 8 月 28 日回复已修复,但 8 月 29 日仍有 Windows 循环/卡死、约两分钟卡住的后续反馈——“已关闭”不等同于覆盖所有客户端的连续播放验收。
  • 816 号 issue 有“线路 4 正常”的样本反馈。它提示 CDN 差异值得测量,但不等于所有用户永久固定线路 4 就是正确做法。

最终修复路线(按证据推进)

  1. 相同房间、码率、CDN、请求头,隔离比较网页模板/上游 WUP 获取方式,单连接连续读流超过三个当前观测失效窗口;记录状态码、结束原因与数据进度,不只验首包。
  2. 若 WUP 明确改善,适配成协议/身份一致、受服务端有效期约束、播放与录制相互隔离的首选源;保留失效恢复,但只在证据支持后削减不必要的定时重开。
  3. 若仍需中途续接,修复实际呈现与音频所有权交接,区分 Android 与 Windows,而不是用固定 40 秒重启充当无缝;保留暂停/关闭代次和失败回滚。
  4. 分别验收媒体连续性、IJK 纹理启动、缓存资源平台期;任何一项失败都不把整项虎牙稳定性标成通过。

验证记录与剩余门禁

验证证据(均为该文档原始记录):

  • 初次红测:缓存继承属性测试实际得到cache-on-disk=yes,与目标no不符——正是“只写预算、没写组合”的失败样本。
  • 一次静态分析通过:No issues found,176.9 秒,记录于20260904T230142251Z-quality-focused.json;本轮不启动手机测试。
  • 暂停竞态红测:resolver 返回后实际安装了新候选而非保留暂停的原播放器,记录于20260904T225019407Z-quality-focused.json。
  • 修复后的 69/69 定向回归通过,覆盖缓存、虎牙 URL、源恢复及候选预热中暂停;记录于20260904T225415949Z-quality-focused.json。
  • 未更改当时的签名优先级、协议租约、40 秒交接定时器;未声称这两项加固已经消除短暂停顿。

后续原生令牌连续播放修复给出了可重复的外网探针操作方式:tool/probes/huya_native_transport_probe_test.dart不参与默认离线测试、不写入媒体或凭据,显式设置PURELIVE_HUYA_LIVE_PROBE=1后经tool/local_ci.ps1 -Scope Focused -TestPath tool/probes/huya_native_transport_probe_test.dart -SkipPubGet运行,可用PURELIVE_HUYA_ROOM、PURELIVE_HUYA_SECONDS(130–600 秒)控制参数。

剩余工作同样被写进门禁:

  • 此轮修复的是缓存配置与明确的换源竞态,不声称 Android/Windows 虎牙短暂停顿已全部消失。
  • 真正连续换源需要分别验证候选媒体时间轴、当前/候选音频所有权、Flutter 实际呈现提交、用户暂停/关闭取消、失败保留旧源与双解码器资源上限。
  • Android 的仅预取 URL 路径仍是已知缺口;不直接移植 Windows 纹理逻辑。
  • 需要跨多个租约周期测量视频最长帧间隔、音频缺口、换源次数、CPU/RSS 和缓存占用;单个首帧测试、接口成功和假播放器测试均不替代这层证据。
  • Bug 批次尚未结束,不作为 3.2.0 稳定版发布;回滚时按独立修复提交回滚,不删除签名、租约和故障恢复保护。
  • 音视频
  • 直播
  • 移动开发

【免费下载链接】pure_live

纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。

项目地址:https://gitcode.com/gh_mirrors/pur/pure_live
点击查看免费下载

相关推荐

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 9:05:28

基于MSP432与OpenMV的自动泊车系统:从硬件连到泊车入位

简介&#xff1a;面向嵌入式与机器视觉学习者的一套自动泊车系统完整工程包&#xff0c;基于MSP432P401R微控制器与OPENMV摄像头&#xff0c;覆盖车位识别、传感器解析、车辆控制及上位机交互等环节&#xff0c;适合课程设计、竞赛备赛或项目入门。压缩包共245个文件&#xff0…

作者头像 李华
网站建设 2026/10/23 23:16:22

代理型工具全解析:从正反向代理原理到Nginx实战

1. 代理型工具是什么&#xff1a;先搞懂这个绕不开的基础设施代理型工具&#xff0c;说白了就是处于用户和真实目标服务之间的那个“中间人”。不管是公司网络里的统一上网出口&#xff0c;还是后端服务前面的统一入口&#xff0c;凡是你把请求先交给它、再由它转发到真正目的地…

作者头像 李华
网站建设 2026/10/17 20:03:31

无人机蜂窝网络:动态覆盖与抗毁性通信方案

1. 项目背景与核心价值去年夏天我在山区自驾时&#xff0c;手机信号时断时续的经历让我开始思考&#xff1a;传统地面基站的覆盖盲区问题是否有更灵活的解决方案&#xff1f;这正是无人机蜂窝网络技术试图攻克的难题。这个项目通过将19个六边形蜂窝组网单元部署在无人机集群上&…

作者头像 李华
网站建设 2026/10/17 13:34:32

基于Python的轻量级驾驶员状态识别系统(闭眼/哈欠/姿态)

简介&#xff1a;本资源是一套完整的驾驶员状态识别深度学习项目实现&#xff0c;面向人工智能初学者、计算机视觉方向学生及智能交通系统开发者&#xff0c;解决驾驶过程中疲劳、分心等危险状态的实时检测问题。项目基于Keras框架&#xff0c;提供VGG16、ResNet50、InceptionV…

作者头像 李华
网站建设 2026/10/20 22:06:14

Android 11 macOS编译失败真相:五层ABI契约与容器化构建方案

1. 为什么Android 11源码在mac上编译会“卡在凌晨三点”——一个被低估的系统级兼容性真相你有没有试过在macOS上拉下AOSP Android 11的完整源码&#xff0c;执行source build/envsetup.sh && lunch aosp_arm64-eng && m之后&#xff0c;眼睁睁看着编译进程在l…

作者头像 李华