cocoa-touch - AVFoundation - AVAssetExportSession - 操作在第二次导出尝试时停止

标签 cocoa-touch avfoundation ios11 avassetexportsession

我正在制作画中画视频,此功能已完美运行(据我所知)1.5 年。现在它出现在 IOS 11 中,它只在第一次被调用时工作......当它被调用来做第二个视频时(没有先强制关闭应用程序)我收到下面的错误消息。

我在堆栈上找到这篇文章,但我已经按照这篇文章正确使用 Assets 跟踪:AVAssetExportSession export fails non-deterministically with error: “Operation Stopped, NSLocalizedFailureReason=The video could not be composed.”

我已经输入了我正在使用的确切方法。任何帮助将不胜感激!

错误信息:

Error: Error Domain=AVFoundationErrorDomain Code=-11841 "Operation Stopped" 
UserInfo={NSLocalizedFailureReason=The video could not be composed., 
NSLocalizedDescription=Operation Stopped, 
NSUnderlyingError=0x1c04521e0 
{Error Domain=NSOSStatusErrorDomain Code=-17390 "(null)"}}

方法如下:

- (void) composeVideo:(NSString*)videoPIP onVideo:(NSString*)videoBG
{
@try {
    NSError *e = nil;

    AVURLAsset *backAsset, *pipAsset;

    // Load our 2 movies using AVURLAsset
    pipAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:videoPIP] options:nil];
    backAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:videoBG] options:nil];

    if ([[NSFileManager defaultManager] fileExistsAtPath:videoPIP])
    {
        NSLog(@"PIP File Exists!");
    }
    else
    {
        NSLog(@"PIP File DOESN'T Exist!");
    }

    if ([[NSFileManager defaultManager] fileExistsAtPath:videoBG])
    {
        NSLog(@"BG File Exists!");
    }
    else
    {
        NSLog(@"BG File DOESN'T Exist!");
    }

    float scaleH = VIDEO_SIZE.height / [[[backAsset tracksWithMediaType:AVMediaTypeVideo ] objectAtIndex:0] naturalSize].width;
    float scaleW = VIDEO_SIZE.width / [[[backAsset tracksWithMediaType:AVMediaTypeVideo ] objectAtIndex:0] naturalSize].height;

    float scalePIP = (VIDEO_SIZE.width * 0.25) / [[[pipAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].width;

    // Create AVMutableComposition Object - this object will hold our multiple AVMutableCompositionTracks.
    AVMutableComposition* mixComposition = [[AVMutableComposition alloc] init];

    // Create the first AVMutableCompositionTrack by adding a new track to our AVMutableComposition.
    AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

    // Set the length of the firstTrack equal to the length of the firstAsset and add the firstAsset to our newly created track at kCMTimeZero so video plays from the start of the track.
    [firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, pipAsset.duration) ofTrack:[[pipAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:&e];
    if (e)
    {
        NSLog(@"Error0: %@",e);
        e = nil;
    }

    // Repeat the same process for the 2nd track and also start at kCMTimeZero so both tracks will play simultaneously.
    AVMutableCompositionTrack *secondTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [secondTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, backAsset.duration) ofTrack:[[backAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:&e];

    if (e)
    {
        NSLog(@"Error1: %@",e);
        e = nil;
    }

    // We also need the audio track!
    AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, backAsset.duration) ofTrack:[[backAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:&e];
    if (e)
    {
        NSLog(@"Error2: %@",e);
        e = nil;
    }


    // Create an AVMutableVideoCompositionInstruction object - Contains the array of AVMutableVideoCompositionLayerInstruction objects.
    AVMutableVideoCompositionInstruction * MainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

    // Set Time to the shorter Asset.
    MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, (pipAsset.duration.value > backAsset.duration.value) ? pipAsset.duration : backAsset.duration);

    // Create an AVMutableVideoCompositionLayerInstruction object to make use of CGAffinetransform to move and scale our First Track so it is displayed at the bottom of the screen in smaller size.
    AVMutableVideoCompositionLayerInstruction *FirstlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:firstTrack];

    //CGAffineTransform Scale1 = CGAffineTransformMakeScale(0.3f,0.3f);
    CGAffineTransform Scale1 = CGAffineTransformMakeScale(scalePIP, scalePIP);

    // Top Left
    CGAffineTransform Move1 = CGAffineTransformMakeTranslation(3.0, 3.0);

    [FirstlayerInstruction setTransform:CGAffineTransformConcat(Scale1,Move1) atTime:kCMTimeZero];

    // Repeat for the second track.
    AVMutableVideoCompositionLayerInstruction *SecondlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:secondTrack];

    CGAffineTransform Scale2 = CGAffineTransformMakeScale(scaleW, scaleH);
    CGAffineTransform rotateBy90Degrees = CGAffineTransformMakeRotation( M_PI_2);
    CGAffineTransform Move2 = CGAffineTransformMakeTranslation(0.0, ([[[backAsset tracksWithMediaType:AVMediaTypeVideo ] objectAtIndex:0] naturalSize].height) * -1);

    [SecondlayerInstruction setTransform:CGAffineTransformConcat(Move2, CGAffineTransformConcat(rotateBy90Degrees, Scale2)) atTime:kCMTimeZero];

    // Add the 2 created AVMutableVideoCompositionLayerInstruction objects to our AVMutableVideoCompositionInstruction.
    MainInstruction.layerInstructions = [NSArray arrayWithObjects:FirstlayerInstruction, SecondlayerInstruction, nil];

    // Create an AVMutableVideoComposition object.
    AVMutableVideoComposition *MainCompositionInst = [AVMutableVideoComposition videoComposition];
    MainCompositionInst.instructions = [NSArray arrayWithObject:MainInstruction];
    MainCompositionInst.frameDuration = CMTimeMake(1, 30);


    // Set the render size to the screen size.
    //        MainCompositionInst.renderSize = [[UIScreen mainScreen] bounds].size;
    MainCompositionInst.renderSize = VIDEO_SIZE;


    NSString  *fileName = [NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"fullreaction.MP4"];

    // Make sure the video doesn't exist.
    if ([[NSFileManager defaultManager] fileExistsAtPath:fileName])
    {
        [[NSFileManager defaultManager] removeItemAtPath:fileName error:nil];
    }

    // Now we need to save the video.
    NSURL *url = [NSURL fileURLWithPath:fileName];
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                                      presetName:QUALITY];
    exporter.videoComposition = MainCompositionInst;
    exporter.outputURL=url;
    exporter.outputFileType = AVFileTypeMPEG4;


    [exporter exportAsynchronouslyWithCompletionHandler:
     ^(void )
     {
         NSLog(@"File Saved as %@!", fileName);
         NSLog(@"Error: %@", exporter.error);
         [self performSelectorOnMainThread:@selector(runProcessingComplete) withObject:nil waitUntilDone:false];
     }];

}
@catch (NSException *ex) {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error 3" message:[NSString stringWithFormat:@"%@",ex]
                                                   delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
}


}

最佳答案

原因: 它最终导致“MainInstruction”timeRange 不正确。

CMTime 对象不能使用“值”进行比较。相反,您必须使用 CMTIME_COMPARE_INLINE。

要修复,请替换此行:

MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, (pipAsset.duration.value > backAsset.duration.value) ? pipAsset.duration : backAsset.duration);

用这一行:

MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTIME_COMPARE_INLINE(pipAsset.duration, >, backAsset.duration) ? pipAsset.duration : backAsset.duration);

关于cocoa-touch - AVFoundation - AVAssetExportSession - 操作在第二次导出尝试时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48129075/

相关文章:

iphone - UITableView 缓慢滚动

objective-c - 如何设置 UIBarButtonItem 的最大宽度

ios - NSTimer 没有正确传递参数?

ios - ARC 的内存问题

ios - 对于 UITableView,我是否需要实现 trailingSwipeActionsConfigurationForRowAt 和/或 editActionsForRowAt?

iphone - 将第一个单元格设置为在 View 加载时突出显示

ios - text 和 textStorage 有什么区别

iOS 11 文件提供程序 fetchThumbnailsForItemIdentifiers 方法未调用

ios - swift 4 : Scanning QR codes with dictionary inputs

ios - dyld:dyld_sim与mach-o xcode 9.0不兼容