1

在发布我的问题之前,我将描述我用单轨创作视频的原因。简而言之,我需要一个具有恒定帧速率的视频,但如果不导出视频并使用 AVMutableComposition 进行合成,我找不到其他方法来做到这一点。

好的,我对此感到非常困惑,因为有时我的视频成功导出,有时没有,当视频未导出时,我收到错误代码 -11841 (AVErrorInvalidVideoComposition)。对我来说,这个错误很笼统,没有描述视频合成无效的原因。

我检查了所有属性,但找不到我的 AVMutableVideoComposition 不正确的原因。

我制作了主要代码来创建视频合成并将其导出。

- (void)saveVideoAtURL:(NSURL *)url withCompletionBlock:(CompletionBlock)completionBlock
{
    NSError *error;
    NSArray *videoTracks;
    NSArray *audioTracks;
    AVURLAsset *asset;
    AVAssetTrack *videoAssetTrack;
    AVAssetTrack *audioAssetTrack;
    AVMutableComposition *composition;
    AVMutableVideoComposition *videoComposition;
    AVMutableCompositionTrack *videoCompositionTrack;
    AVMutableCompositionTrack *audioCompositionTrack;
    AVMutableVideoCompositionInstruction *videoCompositionInstruction;
    AVMutableVideoCompositionLayerInstruction *videoCompositionLayerInstruction;


    self.completionBlock = completionBlock;

    NSDictionary *options = @{
                              AVURLAssetPreferPreciseDurationAndTimingKey:@YES
                              };

    asset                       = [[AVURLAsset alloc] initWithURL:url options:options];
    composition                 = [AVMutableComposition new];
    videoCompositionTrack       = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    audioCompositionTrack       = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

    CMTimeRange timeRange   = CMTimeRangeMake(kCMTimeZero, asset.duration);
    videoTracks             = [asset tracksWithMediaType:AVMediaTypeVideo];
    videoAssetTrack         = videoTracks[0];
    CGSize      renderSize  = videoAssetTrack.naturalSize;
    audioTracks             = [asset tracksWithMediaType:AVMediaTypeAudio];
    audioAssetTrack         = audioTracks[0];

    [videoCompositionTrack insertTimeRange:timeRange ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error];
    if(error)
    {
        DLog(@"Error: %@", [error localizedDescription]);
        self.completionBlock(NO, error);
        return;
    }

    [audioCompositionTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:kCMTimeZero error:&error];
    if(error)
    {
        DLog(@"Error: %@", [error localizedDescription]);
        self.completionBlock(NO, error);
        return;
    }

    // There is a very important instruction, without this, the video will be blank.
    videoCompositionLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoAssetTrack];

    // Instructions about the video composition
    videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

    // Setup the video composition
    videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.frameDuration = CMTimeMake(1, 30);
    videoComposition.renderSize = renderSize;
    videoComposition.renderScale = 1.0;
    videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration);
    videoCompositionInstruction.layerInstructions = @[videoCompositionLayerInstruction];
    videoComposition.instructions = @[videoCompositionInstruction];

    // Get a new url to export the video
    NSURL *outputURL = [self generateOutputURL];

    // Create and exporter and save the video locally
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:[composition copy] presetName:AVAssetExportPresetHighestQuality];
    exporter.outputURL = outputURL;
    exporter.videoComposition = videoComposition;
    exporter.outputFileType = AVFileTypeQuickTimeMovie;
    exporter.shouldOptimizeForNetworkUse = YES;

    [exporter exportAsynchronouslyWithCompletionHandler:^{
        switch (exporter.status) {

            case AVAssetExportSessionStatusFailed:
            case AVAssetExportSessionStatusCancelled:
                DLog(@"Failed %@", exporter.error);
                self.completionBlock(NO, exporter.error);
                break;

            case AVAssetExportSessionStatusExporting:
                DLog(@"Exporting");
                break;

            case AVAssetExportSessionStatusCompleted:
                [self exportDidFinish:exporter];
                break;

            default:
                break;
        }
    }];
}
4

0 回答 0