RPBroadcastSampleHandler在倒计时结束后立即停止——AVAssetWriter在第一帧时崩溃
我需要在我的React Native Expo(bare)iOS项目中,使用ReplayKit的 Broadcast Upload Extension进行屏幕录制的帮助。
当前情况:
我的广播扩展在控制中心的“屏幕录制”下显示正确。
当我选择它时:
- 三秒-二秒-一秒倒计时开始
- 录制开始
- 但它会瞬间停止
因此广播实际上并未正常运行。
因为这个原因:
- 当我返回应用并调用stopRecording()
- App Group的 UserDefaults键(lastRecordingPath)为空
- 未返回视频路径
看起来像是:
- broadcastStarted被触发
- 但扩展立即停止
- 因此broadcastFinished可能会立刻被调用
- 或扩展在第一帧就崩溃
基于调试,我怀疑崩溃发生在processSampleBuffer收到第一帧视频并调用setupWriter() 时,此时使用以下参数初始化AVAssetWriter:
AVAssetWriter(outputURL: url, fileType: .mp4)
项目结构:
主应用
Broadcast Upload Extension(SampleHandler.swift)
App Group已配置并共享
扩展已正确嵌入(Embed & Sign)
启用自动签名
在物理设备测试(iPhone 11,iOS 18.2)
Bundle IDs:
主应用:com.athelia.app
广播扩展:com.athelia.app.AtheliaBroadcast
扩展在控制中心可见,因此配置似乎是正确的。
主要问题:
录制开始但立即停止。视频路径从未保存到App Group
崩溃日志显示(来自设备上的分析数据):
异常:EXC_CRASH (SIGABRT) -[AVAssetWriterInput markAsFinished] closure #1 in SampleHandler.broadcastFinished() consecutiveCrashCount: 3
来自主应用的日志:没有出现过任何 [EXT] 日志,确认扩展在 broadcastStarted 打印任何内容之前就崩溃了:
触发广播选择器按钮
广播检测轮询1:isActive=false ... 🔍 广播检测轮询20:isActive=false ⚠️ 广播检测超时
SampleHandler.swift:
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
let container = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.athelia.app"
)
let baseURL = container ?? FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = baseURL.appendingPathComponent("broadcast_\(Int(Date().timeIntervalSince1970)).mp4")
outputURL = url
UserDefaults(suiteName: "group.com.athelia.app")?.set(true, forKey: "broadcastIsActive")
}
override func broadcastFinished() {
guard sessionStarted,
let writer = assetWriter,
writer.status == .writing else {
saveResultAndExit(path: nil)
return
}
videoInput?.markAsFinished() // guarded — should never crash
audioInput?.markAsFinished()
writer.finishWriting { ... }
}
问题: 为什么在倒计时结束后广播扩展会立即停止?是什么原因导致 broadcastFinished 在没有接收到任何帧的情况下,在 broadcastStarted 之后立即被调用?
解决方案
我只有你提供的这些细节,所以基于此。以下是似乎正在发生的情况。
为什么广播会立即停止
崩溃日志指向broadcastFinished() 内的markAsFinished,但这很可能是早期崩溃的结果,而非根本原因。当扩展崩溃时,系统在清理过程中会调用broadcastFinished()。如果随后对从未接收过样本的输入调用markAsFinished(),可能会再次崩溃。因此真正的问题是在处理任何帧之前就已经发生的初始崩溃。
聚焦于找到初始崩溃点
失败很可能出现在以下某个位置:
- broadcastStarted – App Group容器为nil,或者输出URL不可写。
- processSampleBuffer – 当第一帧到达并执行setupWriter() 时(例如AVAssetWriter初始化失败)。
- First sample append – 当你尝试把第一帧缓冲区追加到写入器时。
如何缩小范围
- 在broadcastStarted中 – 记录App Group容器是否非nil,以及是否能向输出URL写入测试文件。
- 在setupWriter中 – 在创建AVAssetWriter之前记录日志,并将初始化放在do/catch中以记录任何错误。
- 在processSampleBuffer中 – 在接收到第一帧视频样本时记录日志,以及在调用append() 时记录。
需要验证的事项
- App Group – 扩展目标是否具备App Groups功能并使用group.com.athelia.app。如果containerURL(forSecurityApplicationGroupIdentifier:) 返回nil,扩展沙箱中的回退到文档目录可能不可写。
- AVAssetWriter init – 确保URL有效且可写。记录或处理任何初始化失败。
- 扩展内存 – 广播扩展有严格的内存限制(约30MB)。大量分配可能导致系统终止扩展。
一旦你知道是哪一步失败,就可以针对修复。如果你能提供日志停止的位置,我们可以进一步缩小范围。