iOS 16下使用AVFoundation导出视频完成后应用卡顿问题
解决方案:iOS 16下AVAssetExportSession加animationTool导致UI冻结问题
根据你提供的调用栈,主线程卡在Core Animation事务提交阶段,这说明导出完成后animationTool相关的Core Animation资源没有正确处理,导致主线程阻塞。以下是几个可行的修复方案:
1. 导出完成后强制清理animationTool并刷新CATransaction
在导出完成的回调中,先清空AVVideoComposition的animationTool引用,然后手动触发CATransaction刷新,确保所有pending的动画事务都被处理,避免主线程阻塞:
// Objective-C示例 [exportSession exportAsynchronouslyWithCompletionHandler:^{ // 处理导出结果(成功/失败) dispatch_async(dispatch_get_main_queue(), ^{ // 清空animationTool引用 self.videoComposition.animationTool = nil; // 强制刷新CATransaction [CATransaction flush]; // 后续的UI更新操作放在这里 }); }];
// Swift示例 exportSession.exportAsynchronously { [weak self] in // 处理导出结果 DispatchQueue.main.async { self?.videoComposition?.animationTool = nil CATransaction.flush() // 执行UI更新 } }
2. 严格区分Core Animation操作与导出操作的线程
AVVideoCompositionCoreAnimationTool依赖Core Animation的layer对象,这类对象是线程绑定的,必须在主线程完成初始化、绑定到videoComposition的操作;仅将导出任务本身放在后台队列执行:
// 主线程创建并绑定animationTool AVVideoCompositionCoreAnimationTool *animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; self.videoComposition.animationTool = animationTool; // 后台队列执行导出逻辑 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:self.asset presetName:AVAssetExportPresetHighestQuality]; exportSession.videoComposition = self.videoComposition; // 设置其他导出参数(输出路径、文件类型等) [exportSession exportAsynchronouslyWithCompletionHandler:^{ // 后续处理同方案1 }]; });
3. 替换AVAssetExportSession为AVAssetWriter(彻底规避系统bug)
如果上述方案仍无法解决问题,大概率是iOS 16中AVAssetExportSession与animationTool组合存在系统级bug。此时可以改用更底层的AVAssetWriter实现导出,完全自主控制合成流程:
- 创建
AVAssetWriter及对应的AVAssetWriterInput - 创建绑定好
animationTool的AVVideoComposition - 使用
AVAssetReader读取源视频数据,配合AVVideoCompositionRenderContext将带动画的帧写入AVAssetWriter
这种方式代码量更大,但能彻底避开AVAssetExportSession的潜在问题,稳定性更高。
4. 导出完成后触发主线程RunLoop处理未完成事件
如果主线程卡在RunLoop等待事务,可以在回调中手动触发RunLoop处理当前pending事件:
dispatch_async(dispatch_get_main_queue(), ^{ self.videoComposition.animationTool = nil; // 让主线程RunLoop处理完所有待执行事件 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]; });
内容的提问来源于stack exchange,提问作者Rafi Martirosyan
相关产品推荐
相关产品推荐

