Branch:
along
along
along-test
main
memory-center-impl-v1
memory-design-v0.2
${ noResults }
3 Commits (along)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
a9ce8bcdef |
广播扩展改走 127.0.0.1 环回 TCP,去掉 App Group 依赖
上一版用 App Group + Unix domain socket 传音频,结果签不出包: - com.yimai.eaimar 的 profile 不带 App Groups capability - com.yimai.eaimar.BroadcastExtension 没有任何 profile 而 Xcode 又登录不上账号去申请(-1200)。实测原因是网络层: developer.apple.com 通(200),appleid.apple.com 与 developerservices2.apple.com 都连不上(000)——后两个正是登录与签发 profile 要走的域名。 改成环回 TCP 之后不需要任何 entitlement:扩展和主 App 在同一台设备上, 127.0.0.1 天然互通,而且不触发 iOS 的「本地网络」隐私弹窗(那个只针对局域网, 环回是明确豁免的)。主 App 的 entitlements 回到原样,用已有的 profile 就能签, 扩展的 profile 由自动签名就地生成。已真机安装成功。 代价:没有 App Group 就没有地方发布「主 App 这次监听在哪个端口」,只能约定 一个固定端口(52781,两边手工同步,注释里写明了)。撞端口时主 App bind 会 失败并打日志,不是静默失败。 顺带补的两个 socket 选项: - SO_REUSEADDR:上次进程被杀后端口停留在 TIME_WAIT,不开的话几十秒内 bind 都会 EADDRINUSE,表现是「刚重启 App 就用不了」。 - TCP_NODELAY:音频要低延迟,攒包没有意义。 - 只绑 127.0.0.1 而不是 INADDR_ANY,否则这个端口对局域网可见。 另外记两个这次踩到的构建坑: - flutter clean 会删掉 ios/Flutter/ephemeral/,其中的 swift_sdk_local 软链由 Podfile 的补丁在 pod install 时创建、**flutter 不会自己重建**,于是后续 xcodebuild 直接卡在 "Could not resolve package dependencies"。清理之后要 补跑一次 pod install。 - 跑过 --no-codesign 之后再跑签名构建,会复用 build/ 里未签名的 framework, 装机时报 objective_c.framework "invalid signature"。中间做过 no-codesign 验证的话,出正式包前要先 flutter clean。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
8377745992 |
iOS 多媒体翻译接入 ReplayKit 广播扩展,真正拿系统音频
iOS 不允许一个 App 读另一个 App 的音频,Broadcast Upload Extension 是系统
留的唯一口子。链路:
其他 App 播放 → 系统录屏广播 → BroadcastExtension(独立进程)
收 RPSampleBufferType.audioApp → 转 16k/mono/Int16
→ App Group 容器下的 Unix domain socket
→ 主 App BroadcastAudioReceiver
→ azureAsrHelper.audioStream.saveAudioDataTo() ← 已有的 external 通路
→ ASR → 翻译 → 弹幕
新增:
- ios/BroadcastExtension/{SampleHandler,BroadcastAudioSocket}.swift、Info.plist、
entitlements、Extension.xcconfig
- azure_speech 插件的 BroadcastAudioReceiver.swift + 三个 method channel
(startBroadcastAudioReceiver / stopBroadcastAudioReceiver / presentBroadcastPicker)
- Runner.entitlements 加 App Group;xcodeproj 加 BroadcastExtension target
并在 Runner 里内嵌
对上层复用已有的 requestScreenCapture/stopScreenCapture 接缝(Android 是
MediaProjection,iOS 是广播扩展),控制器里两端走同一条分支。
几处踩过的坑,都写进了注释:
- SOCK_STREAM 不保留消息边界,帧必须带 4 字节长度前缀,否则喂给 ASR 的是
错位样本。
- CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer 不拷贝数据,只把
mData 指向 blockBuffer 的内存;blockBuffer 出作用域即释放,交出去就是野指针。
改成在 withExtendedLifetime 里 memcpy 出来。
- AudioBufferList 的 buffer 个数要按 interleaved 与否算,写死 1 会让非交错
立体声整帧拿不到。
- sun_path 只有 104 字节,socket 只能放在 App Group 容器根下,不能建子目录。
- 扩展侧 connect 失败要限流重试(1s),每帧重连会把扩展 CPU 打满,而它的
内存上限只有 50MB。
- SO_NOSIGPIPE:主 App 挂掉时写 socket 默认会直接杀死扩展进程。
- media 这个 ASR 模式的会话配置必须排在 audioSourceType 判断之前,且要
.mixWithOthers —— 否则我们一 setActive 就把正在播放的那个 App 掐了。
- Embed App Extensions 阶段必须插在 Thin Binary 之前。追加到最后会和
Flutter/CocoaPods 的脚本阶段构成依赖环,Xcode 报 Cycle inside Runner。
⚠️ 尚未打通签名:需要在苹果开发者后台登记 App Group group.com.yimai.eaimar、
给 com.yimai.eaimar 开 App Groups capability、并为
com.yimai.eaimar.BroadcastExtension 建 App ID。Xcode 当前还登录不上账号
(-1200)。在这之前 iOS 包签不出来。代码已用 --no-codesign 验证可编译、
appex 也正确内嵌进 PlugIns/。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
2d1ee0b783 |
apps/client 首次入库(Flutter 移动端)
从 /Users/yunyanzhineng/work/eaimar_client-main 搬入,未带原 .git,历史留在原仓库。 3215 个文件 / 约 118MB,其中约 40MB 是构建必需的预编译 SDK(azure_speech 的 MicrosoftCognitiveServicesSpeech.xcframework、device_jieli 的 JL_BLEKit 等)。 build/ (5.2G)、ios/Pods/ (311M)、android/.gradle/ (120M) 已由 .gitignore 挡掉。 本次入库的代码包含两项刚做完的改动: 1) 游客登录入口接上服务端开关。此前 _checkGuestLoginConfig() 恒定写死 true, 入口根本不读配置。现改为取服务端算好的结论,并把入口所在的 Builder 改成 Obx ——配置是异步拿到的,Builder 只渲染 onInit 时的初值 false,入口再也不会出现。 拿不到配置时按隐藏处理:默认显示会在送审期把不该露的入口露给审核员。 ⚠️ 已发布的老客户端关不掉(写死 true),只对本版及以后生效。 2) iOS 上架合规修复: - 移除 ATT/IDFA:删掉 Info.plist 的 NSUserTrackingUsageDescription、 SKAdNetworkItems、NSAdvertisingAttributionReportEndpoint,删掉 splash 里的 授权请求与 app_tracking_transparency 依赖。本 App 从不读取 IDFA,也没有任何 广告/归因 SDK,弹框纯粹劝退用户,还要在 App Store Connect 申报追踪数据类型。 - ios/Podfile 里那段 config.build_settings.delete('NSUserTrackingUsageDescription') 是无效代码(该 key 在 Info.plist 里,不是 build setting),换成注释说明。 - 通讯录符号(ITMS-90683 被拒原因)随之一并解决:permission_handler 走 SPM 后 按 Info.plist 有无对应 key 决定编不编,判定结果被 SwiftPM 按内容哈希缓存, 改完 plist 必须清 DerivedData 与 manifests 缓存才会重新求值。 另附 GOOGLE_PLAY_RELEASE_CHECKLIST.md:Google Play 上架阻塞项的实测梳理 (targetSdk 36 与 Billing 8 的 2026-08-31 期限、16KB 页对齐不合规的两个 .so、 支付回退逻辑的下架风险)。 ⚠️ android/app/sign/ 下的 eaimar.keystore 与 Eaimar.properties(含明文签名密码) 随本次提交入库,与本仓库"私有仓库下接受真实凭据"的既有取舍一致。 仓库若要公开,这两个必须先摘出去。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
2 months ago |