iOS11下AVAssetExportSession二次导出报错Operation Stopped求助
解决iOS 11画中画视频合成二次调用报错问题
我之前维护类似视频合成功能时,碰到过完全一样的iOS 11专属问题——首次调用正常,二次调用就抛出AVFoundationErrorDomain Code=-11841(底层错误码-17390对应kFigAssetWriterError_InvalidInput)。结合你的代码和错误信息来看,核心原因是iOS 11的AVFoundation对资源生命周期管理更严格,你的代码存在未正确释放的合成资源,导致二次调用时出现资源冲突。
问题分析
错误码-17390明确指向「无效的输入资源」,结合你的场景拆解:
- 首次调用后,
AVAssetExportSession、AVMutableComposition等对象未被彻底释放,iOS 11的ARC回收机制没有及时清理这些占用轨道/文件的资源; - 固定的临时文件名
fullreaction.MP4可能在首次导出未完成时,被二次调用的删除操作干扰,引发写入权限冲突; - 局部声明的
AVAssetExportSession可能在后台导出时被提前回收,导致合成中断。
针对性解决方案
1. 强引用并管控ExportSession生命周期
把AVAssetExportSession改为实例变量,避免局部变量被提前回收,同时在二次调用前终止未完成的导出:
// 在你的类头文件中添加属性 @property (nonatomic, strong) AVAssetExportSession *currentExporter; // 在composeVideo:onVideo:方法开头添加检查 - (void) composeVideo:(NSString*)videoPIP onVideo:(NSString*)videoBG { @try { // 终止正在运行的导出任务 if (self.currentExporter && self.currentExporter.status == AVAssetExportSessionStatusExporting) { [self.currentExporter cancelExport]; self.currentExporter = nil; } // ... 原有代码(加载资源、创建Composition等) // 创建ExportSession时赋值给实例变量 self.currentExporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:QUALITY]; self.currentExporter.videoComposition = MainCompositionInst; self.currentExporter.outputURL=url; self.currentExporter.outputFileType = AVFileTypeMPEG4; [self.currentExporter exportAsynchronouslyWithCompletionHandler: ^(void ) { NSLog(@"File Saved as %@!", fileName); NSLog(@"Error: %@", self.currentExporter.error); [self performSelectorOnMainThread:@selector(runProcessingComplete) withObject:nil waitUntilDone:false]; // 导出完成后清理资源 self.currentExporter = nil; }]; } @catch (NSException *ex) { // ... 原有异常处理 self.currentExporter = nil; // 异常时也要清理 } }
2. 生成唯一临时文件名避免写入冲突
替换固定的fullreaction.MP4为UUID命名的文件,避免二次调用时文件被占用:
// 生成唯一文件名 NSString *uniqueFileName = [NSString stringWithFormat:@"fullreaction_%@.MP4", [[NSUUID UUID] UUIDString]]; NSString *fileName = [NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName]; // 原有删除检查可以保留,但因为文件名唯一,其实可以省略 if ([[NSFileManager defaultManager] fileExistsAtPath:fileName]) { [[NSFileManager defaultManager] removeItemAtPath:fileName error:nil]; }
3. 用Autoreleasepool强制回收合成资源
把整个合成逻辑包裹在@autoreleasepool中,确保方法结束后所有局部资源被及时释放:
- (void) composeVideo:(NSString*)videoPIP onVideo:(NSString*)videoBG { @autoreleasepool { @try { // ... 原有所有代码放在这里 } @catch (NSException *ex) { // ... 原有异常处理 } } }
4. 增加轨道有效性检查
避免因轨道为空导致的潜在错误(虽然你检查了文件存在,但视频可能无有效轨道):
// 加载PIP视频轨道时 NSArray *pipVideoTracks = [pipAsset tracksWithMediaType:AVMediaTypeVideo]; if (pipVideoTracks.count == 0) { NSLog(@"PIP video has no valid video track!"); return; } AVAssetTrack *pipVideoTrack = pipVideoTracks.firstObject; // 加载背景视频轨道时同理 NSArray *bgVideoTracks = [backAsset tracksWithMediaType:AVMediaTypeVideo]; if (bgVideoTracks.count == 0) { NSLog(@"Background video has no valid video track!"); return; } AVAssetTrack *bgVideoTrack = bgVideoTracks.firstObject; // 插入轨道时使用上面的变量 [firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, pipAsset.duration) ofTrack:pipVideoTrack atTime:kCMTimeZero error:&e];
5. 修复时间范围的逻辑错误
你原有代码中取较长的duration作为合成时间范围,这会导致其中一个视频轨道超出范围,应该取较短的那个:
CMTime minDuration = CMTimeCompare(pipAsset.duration, backAsset.duration) == -1 ? pipAsset.duration : backAsset.duration; MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, minDuration);
总结
这些改动针对iOS 11的AVFoundation特性做了适配,核心是解决资源泄漏和文件冲突问题。你的代码在高版本iOS上正常,是因为后续版本优化了资源回收机制,但iOS 11对这类问题的容忍度更低。按上面的方案调整后,二次调用的报错应该就能解决了。
内容的提问来源于stack exchange,提问作者berr08
相关产品推荐
相关产品推荐

