1

Before post my question, I will describe the reason of I'm compose a video with a single track. In short terms, I need a video with constant frame rate and I not found another ways to do that without export video and compose it with AVMutableComposition.

Ok, I'm very confusing with this, because sometimes my video are successful exported and sometimes not, when the video isn't exported, I got the error code -11841 (AVErrorInvalidVideoComposition). For me, this error is very generic and don't describe the reason why the video composition is invalid.

I checked all attributes but I can't find the reason why my AVMutableVideoComposition is incorrect.

There is the main code I've made to create video composition and export that.

- (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;
        }
    }];
}
Jan Cássio
  • 2,076
  • 29
  • 41

0 Answers0