ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

pure_live Android AVC 停止边界修复:从 FLV 尾部缺帧到 3.1.8 候选构建验证

pure_live Android AVC 停止边界修复:从 FLV 尾部缺帧到 3.1.8 候选构建验证 音视频直播移动开发【免费下载链接】pure_live纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。项目地址https://gitcode.com/gh_mirrors/pur/pure_live点击查看免费下载导读本文围绕 pure_live纯粹直播录制链路中一个隐蔽但关键的媒体缺陷展开当用户在 FLV 直播录制过程中手动停止时如果已经转发的 AVC 数据尾部仍欠一个画面picture旧逻辑会立即关闭输入导致最终 MP4 在严格解码时因缺少画面的访问单元而失败。本文完整梳理该问题的发现过程、根因定位、常量内存修复设计、终止证据契约以及修复后 16 组 Android 原生场景对应的累计候选构建3.1.84121的完整验证与验收口径并给出对应的源码级实现依据。读完本文你可以掌握FLV 标签边界与 H.264 Annex-B 访问单元边界的关系、pure_live 录制停止预算与排空语义的实现、以及该项目候选发布前质量门禁与构建核验的完整流程。一、问题发现克拉克拉双协议原生录制审计修复的起点来自一次真实平台的双协议录制审计详见 克拉克拉双协议原生录制与尾部缺帧审计。通过注册的 KilakilaSite 萌星目录选择公开免费 UID分别调用生产链路的StreamResolverService、请求头工厂、命令生成器、FFmpegManager与 MP4 转存服务进行 HLS/FLV 双协议短录HLS4 段、源大小 1,035,692 B成品 MP4 852,088 B / 32.864 秒H.264 320×240 AAC严格完整解码退出 0、stderr 为空FLV采集停止code0、inputDrainedtrue、forcedCancelfalse转存也code0但成品 MP4659,962 B 严格解码失败——失败 MP4 有 257 个视频包最后一包在偏移 659,925、长度仅 37 B只含长度前缀 33 B 的 type-6 SEI没有 VCL 画面报错missing picture in access unit with size 37。该案例同时暴露了一个保护盲点FLV 停止与转存都返回 code0旧的完整性分类器没有识别出这个精确的缺帧解析错误导致原始 TS 分片在转存误报成功后被删除MP4 虽保留但已无法修复。这一发现直接否定了FLV 标签完整即录制成功的假设。1.1 独立解码检查自身的校正首次检查只断言退出码HLS 虽退出 0默认 null 输出时基仍有 DTS 提示。随后检查 MP4 全部视频 PTS 严格递增最小正间隔 3000/90000 秒并在改用-fps_mode passthrough -enc_time_base demux后对同一未改变文件全解码HLS 退出 0 且 stderr 为空FLV 仍因缺帧失败。该设置保留解码输出时基不过滤错误、删帧或修改原文件且 stderr 必须为空被加入断言。初版联网运行的原始探针另存为native-probe-first.dart避免用后改脚本冒充已运行输入。二、根因FLV 标签完整 ≠ AVC 访问单元完整通过保存的原始完整 FLV 标签12.041 秒、316,784 BSHAFB26AE91222B6639B2C3DFB8FAA7B5FFD5F1E441530A0DAB67962BA1F9695596共 688 标签加文件头其中 42 个视频标签在画面 NAL 后仍有 SEI做不依赖新直播时序的边界复现用完整标签前缀 独立 FFmpeg CLI 做 FLV→TS→MP4 与完整解码截止点输入字节采集/转存退出严格完整解码含尾 SEI 视频标签之前80,0090 / 00错误为空该完整视频标签之后80,3380 / 0缺帧失败再包含下一视频标签82,4880 / 00错误为空结论FLV 标签完整不保证后续 Annex-B 访问单元尾部完整。维护分支引入的 FLV 输入桥解决了半个标签被截断的问题但把完整 FLV 标签当作足够的停止条件已保存样本含 picture 后的 SEI下游 H.264 Annex-B parser 把它视为下一访问单元的开始停止在此处会留下没有画面的尾包。这类过弱边界假设属于fork-regression新平台触发旧假设冻结上游未发生 fetch/merge。三、修复设计常量内存的 AVC 访问单元边界观察器修复提交1abbff9abc78e698f8bb24e6a542bb2ecf35484d没有走把失败改成错误提示的捷径而是完成已复现边界的实际修复。核心实现位于 ffmpeg_flv_input_relay.dart 中的FlvAvcAccessUnitBoundary按 AVC 配置声明的 1/2/4 字节 NAL 长度扫描标签AVCDecoderConfigurationRecord的lengthSizeMinusOne长度 3 为 AVC 配置保留值直接视为无效只读 NAL 头不解码、不积累整场直播、不猜其他 codec 格式遇到 type 1/2/5非 IDR / 分割 / IDR 画面则清除待画面状态遇到 type 6~9SEI/SPS/PPS/AUD则置为待画面畸形长度置为不确定状态且不伪造完整边界音频、脚本标签与正常媒体内容全部原样转发没有重排 NAL、全局过滤 SEI、截掉画面或转码用户停止时若已转发的前缀仍等待画面则继续读到下一个不再留下待完成前缀的完整画面边界同一网络块中后续标签不再转发。正常边界和连接前停止仍立即关闭输入。final type tag[offset] 31; if (type 1 || type 2 || type 5) { pending false; // 画面到达访问单元完整 } else if (type 6 type 9) { pending true; // SEI/SPS/PPS/AUD下一访问单元前缀 }配套的FlvInputFramer只负责 FLV 帧定界11 字节标签头 DataSize 数据 PreviousTagSize单标签最大 0xffffff 载荷无生命周期缓冲增长转发时逐标签原样提交给下游 native reader。四、停止语义与终止证据契约停止侧仍使用原有3000 ms服务级停止预算inputDrainBudgetMs: drainKind flv ? 3000未增加定时等待或重试。终止证据在 ffmpeg_service.dart 的terminalEvidence()中集中生成inputDrained: finishRequested !forcedCancel flvInputRelay?.hasPendingAccessUnit ! true, if (flvInputRelay ! null) flvAccessUnitPending: flvInputRelay!.hasPendingAccessUnit, inputIntegrityError: liveRecording (hasInputPacketError || flvInputRelay?.hasPendingAccessUnit true || FFmpegMediaIntegrity.hasPacketError(fallbackLogs)),关键语义变化inputDrained不再把已知 AVC 未完成情况写成排空成功新增flvAccessUnitPending终止证据字段并沿既有inputIntegrityError持久化/源保留路径处理未新增任务数据 schema先前缺帧错误保留源保护继续有效看不到完整边界、网络停滞或上游提前 EOF 时保留不完整标志和源分片而非悄悄删掉待完成元数据正常时间戳修正、停止 I/O、原生线程退出提示仍按现有分类处理不 blanket 拦截警告新 session 不继承前次损坏。本次修复不承诺缺失后续画面的源流自动变成完整录像——如果所有后续标签仍留下前缀或期限内未取得完整边界结果仍明确失败/保留源不把这种情况计为健康录制。五、回归验证52 项定向回归与四场景固定输入修复后进行 52 项定向回归含长度、配置变化、未知/畸形输入、正常画面/填充尾部、连续输入和终止证据合同两个真实 loopback 红项在旧代码上均失败停止立即结束而非等待画面随后52/52 通过、五文件 analyze 104.1 秒无问题。增加只读转发字节计数核验实际输入范围后最终仍 52/52。原生探针使用保存的 316,784 B FLVSHA 未改变固定在 6 秒附近触发停止使用真正的FFmpegManager、输入桥、转存服务与独立全解码不以假原生执行器代替场景停止前输入 / 最终转发停止实测结果正常画面边界158140 / 158140 B23 msMP4 成功完整解码 0、错误为空尾部有前缀随后提供完整画面158469 / 163709 B16 ms原字节保留MP4 成功完整解码 0、错误为空上游停滞158469 / 158469 B3020 ms原 3 秒预算后强制停止TS 保留、无最终 MP4上游提前 EOF158469 / 158469 B11 ms不假报排空TS 保留、无最终 MP4两种缺失输入均inputDrainedfalse / inputIntegrityErrortrue正常两例为 true / false重放服务按门闩批量供给后续字节16 ms 仅为这次固定输入的实际耗时不是公网帧等待保证。四例均结束实际原生会话并释放转存状态全部通过1 个原生集成测试总耗时 387.657 秒含排队/编译。六、真实生产双协议复测通过当前注册适配器重新获取萌星目录、主播 UID 和签名输入HLS 选择当前样本FLV 以同 UID 重新解析不复用旧签名 URL也不与先前固定输入混为同一广播使用生产请求头、命令、原生采集、分片转存及完整解码探针输入时间 UTC成品时长源片数 / 源大小MP4 字节 / SHA-256HLS04:10:1432.872 s4 / 1,047,348 B864,615 B /EA59DD0A53E87F2600853548C2693FCB8E6DC0347B57DE13D9B17B0E5BD3DA78FLV04:12:1928.933333 s3 / 947,520 B746,070 B /0C8A9A87BE3929969C5A47B6418C4EEF929EF5BD0600419D386557AF0CD6B1C9两者均为 H.264 320×240 AAC完整成品解码退出 0 且 stderr 为空停止后原生会话与转存操作释放正常源片工作副本在提交后删除。FLV 此次停止 246 ms、forcedCancelfalse、inputDrainedtrue、flvAccessUnitPendingfalse。真实短录没有覆盖所有随机停止点由上述固定边界重放提供确定性对照HLS 1/1、FLV 1/1。七、累计 Android 候选构建与完整核验修复与坏源保留保护26de4378见 克拉克拉原生尾部审计连同先前萌星目录修复汇总到干净提交966735384aa3224f217fa14eb4e72769a6edafac完成一次完整质量门禁及 Android arm64 Debug 构建详见 AVC 停止修复累计 Android 候选。7.1 质量门禁Flutter 3.47.0 / Dart 3.13 固定入口锁文件未改变2098/2098 Flutter 测试、42/42 公共接口通过全量 analyze 一次、233.3 秒无问题质量记录20260908T042959828Z-quality-full.json572.715 秒峰值 CPU 44.01%、13,039,648,768 B结束活跃重型进程 0仓库检查 4301 个跟踪文件、0 错误、2 个既有提示已审阅公开 TLS 测试私钥和空 catch 清单本轮原生依赖预取及 Windows FFmpeg hook 均报告 SHA 校验通过没有升级 SDK、插件或原生库。7.2 Android arm64 Debug 构建产物构建记录20260908T043750521Z-build-androidarm64-debug.json1042.431 秒含前置质量阶段Gradle 413.2 秒workers16峰值 CPU 75.48%、14,141,349,888 B包名com.mystyle.pureliveversionName3.1.8基础 build4121arm64 偏移 2000Manifest versionCode6121唯一 ABI arm64-v8a16 个原生 ELF 的 LOAD 对齐至少 0x4000APK 对齐、1262 项 Flutter 资源、版本/翻译与关键原生库完整性门禁通过实际中英文萌星名称与能力说明逐值核对测试 TLS 私钥未打包大小288,139,348 BSHA-25645D2D8C2386F4F86CAC83917472960D1725599FB5F03AA6852E039D331E0D84E签名校验通过证书 SHA-2561e832295a696cf8210bca063e458bad0be12dfa64939d3362ff11b8f237ff7b9与先前 ae5232b2 归档 Debug 包一致归档位置local-artifacts/candidates/android-96673538/PureLive-3.1.8-4121-android-arm64-v8a-debug.apk复制后哈希复核旧包哈希仍正确未覆盖通用local-artifacts/3.1.8-4121/中其他平台文件属于历史独立构建目录列表不是本轮全平台构建成功。7.3 质量门禁的诚实性细节首次由调用者误选 Windows PowerShell 5.1在解析无 BOM 中文脚本时失败未进入 Flutter analyze/测试或 Gradle保留失败质量记录20260908T041939717Z-quality-full.json与原日志同一源码在实际工作环境 PowerShell 7.6.5 解析、平台合同检查通过后重跑未通过编辑测试、删中文或跳过门禁掩盖错误。构建记录保留增量缓存与 daemonFROM-CACHE0 / UP-TO-DATE0、configuration cache 未复用Firebase 未来 KGP 兼容性警告保留没有借机升级依赖。八、后续验收与交付边界候选保留了既有 15 组累计 Android 原生场景新增本候选 AVC 正常/待画面/停滞/EOF 及源保留验证共16 组待验Windows 已通过的原生固定输入和真实双协议短录继续作为定位证据不替代 Android 原生 FFmpeg 或界面验收。设备操作前仍按当前窗口、共享 purelive 租约、显式 serial、指定设备与当前签名核验执行覆盖安装保留数据不重启、不清数据、不修改网络/Root/LSP。口径澄清未安装、未操作手机、未发布候选通过不是原生/UI/长录验收通过版本保持 3.1.84121Windows GUI 候选仍 f3de664a历史矩阵仍为 62 项20 PASS、32 RUN、10 NR16 组候选专项不与 42 个未闭环大项简单相加或当作剩余 Bug 数12 组参考平台尚未注册全平台/长录/性能和正式 3.2.0 条件继续未提前宣称完成本机证据目录local-artifacts/avc-stop-android-candidate-20260908/保留首次失败、重跑日志、签名结果、源冻结、16 组 native-handoff、校验索引及终态 checkpoint。九、总结从克拉克拉双协议短录中的一次缺帧解码失败到FlvAvcAccessUnitBoundary的常量内存边界观察、flvAccessUnitPending终止证据与源保留保护再到累计 Android 候选的完整构建核验这条修复链贯穿了 pure_live 录制模块发现问题 → 固定输入复现 → 根因修复 → 回归 真实复测 → 候选门禁的完整闭环。其核心工程原则值得借鉴边界判断必须以媒体语义AVC 访问单元为准而非容器语法FLV 标签为准任何完整声明都必须有可审计的终止证据支撑修复不掩盖失败宁可保留源分片与不完整标志也不把坏源误报为健康录制。相关实现可继续深入阅读FFmpeg FLV 输入桥实现、终止证据与完整性分类、录制控制器源保留处理、任务模型完整性字段。赞分享音视频直播移动开发【免费下载链接】pure_live纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。项目地址https://gitcode.com/gh_mirrors/pur/pure_live点击查看免费下载相关推荐Wand-Enhancer 完整指南从零构建 WeMod 本地增强工具到手机面板跑通只需 1 次扫码Wand Enhancer 完整指南从零构建 WeMod 本地增强工具到手机面板跑通只需 1 次扫码 打开设置页看到升级到 Pro和月费的那个瞬间多数桌面应用前端ArchiveBox 自定义 Django Widgets 深度解析TagEditorWidget、InlineTagEditorWidget 与 URLFiltersWidgetArchiveBox 自定义 Django Widgets 深度解析TagEditorWidget、InlineTagEditorWidget 与 URLFi音视频直播移动开发gopass 版本发布全流程指南从 goreleaser 配置、RC 候选到可复现构建与产物验证gopass 版本发布全流程指南从 goreleaser 配置、RC 候选到可复现构建与产物验证 gopass 使用 goreleaser https://l应用安全开发工具上一篇FanControl终极教程Windows风扇智能控制完整指南下一篇5分钟快速上手My-TODOs跨平台桌面待办工具完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进