You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS11下AVAssetExportSession二次导出报错Operation Stopped求助

解决iOS 11画中画视频合成二次调用报错问题

我之前维护类似视频合成功能时,碰到过完全一样的iOS 11专属问题——首次调用正常,二次调用就抛出AVFoundationErrorDomain Code=-11841(底层错误码-17390对应kFigAssetWriterError_InvalidInput)。结合你的代码和错误信息来看,核心原因是iOS 11的AVFoundation对资源生命周期管理更严格,你的代码存在未正确释放的合成资源,导致二次调用时出现资源冲突。

问题分析

错误码-17390明确指向「无效的输入资源」,结合你的场景拆解:

  1. 首次调用后,AVAssetExportSession、AVMutableComposition等对象未被彻底释放,iOS 11的ARC回收机制没有及时清理这些占用轨道/文件的资源;
  2. 固定的临时文件名fullreaction.MP4可能在首次导出未完成时,被二次调用的删除操作干扰,引发写入权限冲突;
  3. 局部声明的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:26:42