Skip to content

fix: 修复小文件传输卡死问题并增加喷泉码离线单文件版本 - #11

Open
zxq1002 wants to merge 20 commits into
topcss:masterfrom
zxq1002:master
Open

fix: 修复小文件传输卡死问题并增加喷泉码离线单文件版本#11
zxq1002 wants to merge 20 commits into
topcss:masterfrom
zxq1002:master

Conversation

@zxq1002

@zxq1002 zxq1002 commented Aug 8, 2026

Copy link
Copy Markdown
  • 修复喷泉码 degree 计算在小文件(L<=12)下未做上限约束导致的死循环 BUG
  • 增加防死循环尝试限制及 0 字节文件/文本处理
  • 新增 100% 离线单文件版本 airscan-fountain-embedded.html
  • 更新 README 补充离线版本说明

zhouxq and others added 11 commits August 8, 2026 14:31
- 修复喷泉码 degree 计算在小文件(L<=12)下未做上限约束导致的死循环 BUG
- 增加防死循环尝试限制及 0 字节文件/文本处理
- 新增 100% 离线单文件版本 airscan-fountain-embedded.html
- 更新 README 补充离线版本说明
- 作为独立高级扩展模块引入 (参考 libcimbar / CFC 实现)
- 支持高密度 4-Color 矩阵、WebAssembly 解码与 Zstd 压缩
- 保持基础版 (index.html/airscan-fountain.html) 的零配置与全兼容特性
- 更新 README.md 添加版本分类说明
- 提供完整的 iOS 原生 App 架构 (SwiftUI + AVFoundation + Objective-C++);
- 引入 WKWebView + 本地 HTTP Server 复用 cimbar WASM 模块方案,保障离线高准确度扫码解码;
- 补全 Xcode 工程、命令行编译脚本 (build.sh)、相关技术评估文档,并更新 .gitignore 忽略 Xcode 编译与用户状态产物。
- 优化 WebReceiver 扫码解码管线,实现 JS 与 Native 实时轮询沟通以流畅渲染解包进度条
- 修复 HUD 界面受 Safe Area 齐平刘海遮挡问题,适配各类 iPhone 机型
- 增加基于 CFC 品牌标识的 1024x1024 高清 AppIcon 图标,并修复消除图标边缘白边残留
接收端打开后相机全屏铺满并盖住进度 HUD,只能手工把视频切成画中画才看得到进度,
而切换后预览又被缩到右下角,页面其余部分只剩黑块。

根因:WKWebViewConfiguration.allowsInlineMediaPlayback 在 iPhone 上默认为 false,
导致 <video> 的 playsinline 属性被忽略,getUserMedia 视频流被系统全屏播放器接管。

- 开启 allowsInlineMediaPlayback、禁用画中画,并让 WebView 与宿主同色以消除加载白闪。
- harness.html 改为上下分区:上方圆角相机取景区(启动遮罩内嵌其中),下方不透明进度
  面板,两者从启动起即同时可见且互不遮挡。
- 删除与网页 HUD 完全重叠的 SwiftUI HUD,原生侧只保留文件预览职责;为其供数的 500ms
  evaluateJavaScript 轮询一并关闭,不再与解码抢占主线程。

计时语义从「相机运行时长」改为「实际接收时长」。底层用累计毫秒 + 当前计时段起点时间戳,
判据复用 recv.js 已有的 active_xhairs 状态(最近 30 帧内成功解出过数据):

- 首次识别到二维码才起表,相机就绪不计时
- 目标丢失即冻结,重新识别从冻结值续计而非清零
- 追加 1.5s 看门狗,帧回调断流时同样暂停
- 接收完成定格总耗时

重置:关闭完成预览为软复位,清空面板并将模式回退为自动探测;进度面板新增重置按钮,
执行整页重载。wasm 未导出解码器 reset,只有重建 Module / Worker / fountain sink
才能真正回到初始状态。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
harness.html
- 重试按钮改为整页重载,不再重入 boot()。recv.js 的 init_ww 只清空 _workers
  数组而不 terminate 旧 worker(每个各带一份 wasm 实例),劫持函数也会逐层
  叠加,且 ww_ready 已 settle 会让 startCamera 抢在新 worker 就绪之前运行。
- 劫持 Recv.set_error 以暴露相机启动失败。getUserMedia 是异步 reject,
  startCamera 的 try/catch 抓不到,recv.js 只把错误写进隐藏的 #errorbox,
  用户会永远停在"请对准二维码"而看不到任何报错与重试入口。

WebReceiverServer.swift
- 目录守卫此前写成 `... || true`,整句恒真,请求目录会走到 Data(contentsOf:)
  抛异常并返回 500 而非 404;改为真正的 isDirectory: 检查。
- 穿越守卫的前缀比较补上尾部分隔符,避免 WebResourcesBackup/ 这类同前缀
  兄弟目录绕过。
- listener 进入 .cancelled 时也 resume continuation,否则 stop() 与启动竞态
  会让调用方永久挂起且错误分支不触发。
- 三段重复的 resumed 判断收敛为单个 finish 闭包,并用 nonisolated 引用盒
  替换捕获 var(本模块默认 MainActor 隔离,捕获 var 在 Swift 6 是错误)。

FilePreviewView.swift
- 文件名来自发送端 zstd 头,属不可信输入:只取 lastPathComponent 并剔除
  分隔符,空或 . / .. 兜底为 received.bin;写入失败改为返回 nil 并显式提示,
  不再静默把一个不存在的 URL 交给分享面板。

.gitignore
- xcuserdata 三条规则含非结尾斜杠而被锚定到仓库根,实际没有忽略
  ios-cfc-client/ 下的目录,加 ** 前缀修正。
- 移除一刀切的 *.png:build/ 与 DerivedData/ 已覆盖构建产物,而该规则会静默
  吞掉未来所有图片资源。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
路线 A(原生 AVFoundation + C++ 解码)自引入起就是一具没有引擎的骨架:
CFC_LIBCIMBAR_BACKEND 恒为 0,真实 libcimbar/OpenCV 从未链接,整段 cimbard_*
调用被编译期切掉,解不出任何一帧;MainView 也从未实例化 ScannerView,
CameraManager / ScannerView / ProgressOverlayView / CFCDecoderBridge /
CFCCoreDecoder 只互相引用,是一座自闭岛屿。

删除而非保留的理由:
- 期权价值接近零。真正值钱的是接入知识,已完整记录在 ALIGNMENT-ANALYSIS.md
  §6;而骨架的胶水层已知三处错误(bytesPerRow 行填充未处理导致逐行错位、
  BGRA 按 RGBA 传入未交换通道、载荷读取仍是 TODO),接入真实引擎时必然重写。
- 持续收费。上一轮代码审查 14 项发现中有 5 项出自这段不可达代码;
  ScannerView / ProgressOverlayView 还与已统一的 WebView 内 HUD 高度重复,
  容易让后续改动改错那一份。
- 体量占比高。约 1200 行,占 Swift/C++ 源码约 63%。
- 可逆。完整实现保留在 commit 7ee408a。

一并清理:
- pbxproj:6 条 PBXBuildFile、8 条 PBXFileReference、App/Camera/Decoder 三个
  已空的 PBXGroup 及其在 Sources 组内的引用、6 条 Sources 构建阶段条目,
  以及两个 target 配置里的 SWIFT_OBJC_BRIDGING_HEADER。
- build.sh:删掉命令行传入的 SWIFT_OBJC_BRIDGING_HEADER,否则脚本会指向一个
  已不存在的头文件。
- 顺带移除两处 Xcode 模板残留:Sources/App/CFCApp.swift、cfc/ContentView.swift
  (真正的 @main 在 cfc/cfcApp.swift)。
- README 目录树按现状重写;ALIGNMENT-ANALYSIS.md 增加 v1.3.0 说明,写明删除
  理由、取回方式,以及"日后原生提速请按 §6 重新实现而非复活骨架"。

验证:清空 DerivedData 后全新构建通过,只编译 6 个 Swift 文件、无任何
C++/ObjC++ 参与;模拟器全新安装运行,界面与清理前一致;build.sh 实跑通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
中英各一个名称必须走 Info.plist 本地化,分两处落地:
- 基准/英文名:两个 target 配置各加 INFOPLIST_KEY_CFBundleDisplayName = Cimbar。
  项目启用了 GENERATE_INFOPLIST_FILE,磁盘上没有 Info.plist,只能用 build
  setting 注入。
- 中文名:新增字符串目录 cfc/InfoPlist.xcstrings(en → Cimbar,
  zh-Hans → 无网码传),并把 zh-Hans 加入 knownRegions,否则该本地化不参与编译。

放在 cfc/ 是因为该组是 PBXFileSystemSynchronizedRootGroup,文件会被自动纳入
资源阶段,无需改动 pbxproj 的资源条目;项目本就启用
LOCALIZATION_PREFERS_STRING_CATALOGS,用 .xcstrings 也省去手写 PBXVariantGroup。

效果:系统语言为中文的设备显示「无网码传」,其余显示「Cimbar」。

验证:全新构建通过,xcstringstool 已编译该目录;产物 Info.plist 的
CFBundleDisplayName 为 Cimbar,zh-Hans.lproj/InfoPlist.strings 为「无网码传」。
桌面图标名未能截图取证(新装 App 落在后续分页,模拟器翻页需合成滑动而
System Events 枚举不到其窗口),该环节依据 bundle 内容与系统既定行为推断。

未改动 PRODUCT_NAME / CFBundleName / PRODUCT_BUNDLE_IDENTIFIER,以免牵动
产物名、脚本路径与签名标识。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
原有 4 条说明里 2 条与当前实现相反,另 1 条移植了网页端的数字:
- 「基于硬件级 AVFoundation 直出像素流」:描述的是已移除的路线 A。现在仓库里
  只剩 AVCaptureDevice.authorizationStatus / requestAccess 用于申请权限,没有
  AVCaptureSession;取景与供帧全部由 WKWebView 内的 getUserMedia +
  requestVideoFrameCallback 完成。
- 「避开 Safari WebKit 渲染失真」:正好说反了,当前实现就跑在 WKWebView 里,
  正是靠它复用网页端 wasm。
- 「1MB 文件约 40s 完成传输」:该数字出自 CIMBAR-TRANSFER-README,描述的是网页端
  cimbar-transfer.html,不是 iOS 端实测。
- 「支持 4C 高密度彩色图标色码」属实(recv.js 的 modeVals 含 4),保留并扩写为
  完整的四模式说明。

改为 6 条可核验的描述,逐条依据:
- wasm 内实测含 cv::Mat / cv::threshold / cv::THRESH_OTSU / cv::solve 等 OpenCV
  符号,以及 fountain_decoder_sink、ZSTD_createDStream;
- harness.html 的 NUM_WORKERS = 3;
- recv.js modeVals = [66, 68, 67, 4] 对应 Bu / B / Bm / 4C;
- 计时行为对齐本分支已实现的「识别到码才起表、丢失自动暂停」。

速度改为标注口径的脚注:iOS 端实测 B 模式、15 FPS 下 1MB 约 32 秒(用户实测提供;
15 FPS 与 recv.js 的 frameRate ideal 默认值一致),并保留网页端约 40 秒的参考值
及其出处,不再让网页端数字冒充 iOS 实测结果。同时把「帧率」列入影响因素。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 新增标准的 MIT License 授权文件,版权归属更正为 AirScan-QR 官方项目
- 依据功能将 index.html 重命名为 airscan-basic.html,index-embedded.html 重命名为 airscan-basic-embedded.html,实现全库文件规范化对齐
- 重新打造版本入口 index.html,支持多版本导航及 10 秒倒计时自动跳转与手动取消功能
- 全面重构 README.md,将版本介绍调整为包含版本分类、原理特性、依赖与场景推荐的 Markdown 矩阵表格
zhouxq and others added 8 commits August 9, 2026 19:45
…function 异常

- 增加 isWasmReady 函数校验 WebAssembly 导出接口有效性
- 配置 Module.onRuntimeInitialized 监听器,并在 WASM 实例化就绪后自动唤醒编码传输按钮
- 在 startEncoding 入口增加异步载入安全防护与友好提示
- 在 cimbar-transfer.html 中实装 WASM 安全检测代理 getWasmFn、组合校验 isWasmReady、250ms 轮询探针及友好异常捕获机制,彻底解决异步加载竞争问题
- 增加高速专业版发送端 (cimbar-send.png)、网页接收端 (cimbar-receive.jpg) 与 iPhone 接收端 (cimbar-receive.ios.png) 真实运行截图
- 在 README.md 中精细调整界面预览表格与图片显示比例,实现排版居中对齐
iOS 接收端此前只认 Cimbar 色码。现在同一个扫码界面并联两条解码链路,
自动识别本仓库的全部三种协议,无需手动切换:

| 协议 | 载荷格式 | 解码方式 |
|------|----------|----------|
| Cimbar 4-Color | —— | recv.js + 3 Worker + WASM(原有) |
| 标准单码 airscan-basic | TaskID\|FileName\|Total\|Index\|Base64 | jsQR → 按索引归位 → 拼接 |
| 喷泉码 4C airscan-fountain | TaskID\|Seed\|Total\|Base64\|Name:Size | jsQR → PRNG 还原索引 → 高斯消元 |

实现要点:

- PRNG / 度数分布 / xor / b2u 与 airscan-fountain.html 逐字节一致。种子还原出
  的块索引集合只要有一点偏差,消元必然失败,因此这几个函数不能"等价重写"。
- 每帧扫 5 个区域(全帧 + 4 象限),对齐网页端的 regions.forEach。喷泉码同屏
  播 4 个不同的码,只扫全帧会让有效吞吐掉到 1/4。
- 两道性能闸门:recv.js 确认锁定 Cimbar 模式,或十字准星点亮时,完全跳过
  jsQR。Cimbar 色码不是 QR,此时 5 个区域必然全部落空,而一次全量扫描约
  145 万像素(640x1137 竖屏画布)足以占满主线程 —— recv.js 也正是在主线程
  向 3 个 Worker 投帧,不设闸门会直接拖慢 Cimbar。
- 视野里可能出现任何一张陌生二维码,载荷一律按不可信输入处理:分派前校验
  Base64 字符集,decodeURIComponent 单独兜底,且整个扫描入口包 try/catch。
  它挂在 recv.js 的每帧回调上,一次未捕获的抛出就会让解码循环永久停摆。
- HUD 增加协议模式显示;换文件(TaskID 变化)时连同计时与进度一起软复位。

vendored jsQR 1.4.0(Apache-2.0,jsDelivr 压缩版),随 WebResources
folder reference 整体进包。

实测(iOS 真机):喷泉码 4C 100KB 约 50 秒,标准单码 30KB 约 50 秒。

验证:node 测试台从 harness.html 抠出真实的 UniversalDecoder 与
scanVideoForQR,用 airscan-fountain.html 的原版发送端算法跑闭环,23 项通过
(50KB 喷泉码还原开销 1.15x、30KB 单码乱序+30% 丢帧还原、协议判别互斥、
畸形输入不抛、5 区域全扫、两道闸门零扫描、节流生效、四象限无缝覆盖)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
模块设置了 SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor,会把
WebReceiverServer 一并推成 MainActor 隔离,于是在网络队列上回调的
newConnectionHandler 里调用 handle(_:) 就成了「在同步非隔离上下文中
调用 MainActor 隔离方法」。

但这个类整套逻辑本就跑在自己的串行队列上:newConnectionHandler、
receive、stateUpdateHandler 全由 Network.framework 在该队列回调,不碰
主线程;真按 MainActor 处理反而会让 HTTP 收发跟 WKWebView 的解码抢
主线程。因此显式标注 nonisolated 退出默认隔离,跨线程安全仍由串行队列
保证(@unchecked Sendable 不变)。

验证:clean build 零警告;模拟器实跑确认本地服务正常起监听并完成
harness.html → wasm ready → workers ready → init_video 全链路,
curl 直连确认 harness.html 与 jsQR.js 均 200 且 MIME 正确。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
逐项与代码核对后修正,主要是路线 A 于 v1.3.0 删除后遗留的陈述,
以及若干可验证的事实性错误。

README.md
- HTTPS 端口 8443 改为 4443(https_server.py:40 实际绑定 4443),共 2 处。
- 删除「AVFoundation 硬件捕获」「SAD Detector 条码方差」「KB/s 速率」
  「传输带宽」等描述:前两项属已删除的路线 A 伪解码器,后两项 HUD 从未显示。
- Worker 数分别标注:网页端 4(cimbar-transfer.html:843),iOS 端 3
  (harness.html NUM_WORKERS),原文写「均为 4」。
- 技术原理重构:原文把 airscan-fountain 的 LT 码 + 高斯消元当成 Cimbar 的
  喷泉码,二者实为不同算法(Cimbar 用 wirehair,本仓库 ALIGNMENT-ANALYSIS
  自己也指出过)。拆为单码 / LT 喷泉码 / Cimbar / iOS 架构四节并补全载荷格式。
- 补环境要求:Xcode 16.0+、iOS 26.5(IPHONEOS_DEPLOYMENT_TARGET 实际值)。

ios-cfc-client/README.md(重写)
- 原「特性亮点」4 条里有 3 条描述的是已删除的路线 A:AVFoundation 60 FPS
  零拷贝、CVPixelBuffer 读取与「4C 识别率提升 300%」、C++ 桥接解码引擎。
  第 3 条声称 HUD 显示 KB/s 与 FPS,实际均无。全部重写为真实能力。
- 修正 xcodeproj 路径(cfc/cfc.xcodeproj 改为 cfc.xcodeproj,前者不存在)。
- 修正 Xcode 版本要求(14.0+ 改为 16.0+,工程 objectVersion=77 且使用文件
  系统同步组),补 iOS 26.5 部署目标。
- 目录结构补齐 build.sh、Assets.xcassets、InfoPlist.xcstrings、jsQR.js。
- 新增解码链路图、实测耗时表与支持的发送端对照表。

ios-cfc-client/ALIGNMENT-ANALYSIS.md
- 版本表补 v1.2.0(路线 B 落地)与 v1.4.0(全协议),原表 v1.1.0 直跳 v1.3.0。
- §1 结论「必须链接 libcimbar 才能收到文件」已被路线 B 推翻,补进展说明;
  结论速览表加注最后一列停在 v1.1.0,非当前实现。
- §3/§4/§5 明确标注为历史章节(所述文件已删除),行为清单本身仍有效,
  故保留而不删除。
- §6 标题由「遗留:必做」改为两条路线的对照,路线 A 标注为未采纳。

ios-cfc-client/WASM-REUSE-FEASIBILITY.md
- 补 v1.2.0:真机端到端验证通过,secure-context 未被拦截,自签 HTTPS 未启用。
- §6 风险表「解码帧率弱于原生 AVFoundation」标为已排除:实测 32 秒快于
  网页端 40 秒,因帧数据完全不跨原生桥。
- §8 补 v1.3.0 决策:路线 A 已删除且不建议再做,并给出两点依据。
- 修正 §5.3/§5.5 中引用已删除 CameraManager / ScannerView 的落地说明。
- 新增 §10 全协议扩展:载荷格式、与网页端的算法一致性要求、两道性能闸门、
  异常隔离与实测耗时。

CIMBAR-TRANSFER-README.md
- iPhone 接收端指南改为首推原生客户端(无需电脑起服务、无需同一局域网、
  无需信任自签证书,且实测更快),浏览器方案降为备选。
- 传输速度区分平台标注;补 cimbar-deps-start/generate_cert.sh。

实测耗时统一为 iOS 端真机数据:Cimbar 1MB 约 32 秒(15 FPS)、
喷泉码 4C 100KB 约 50 秒、标准单码 30KB 约 50 秒。

验证:4 个 mermaid 代码块节点标签均已加引号;全部 md 内部链接 0 断链;
已删除组件的残留引用仅存在于显式标注为历史的章节。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant