Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVAssetExportSession rotation error's when video is from camera roll

I am trying to convert .mov video to .mp4 and at the same time correcting the orientation. The code I use below works great when recording a video using UIImagePickerController however if the video is selected from the camera roll I get this error and I don't see why:

Export failed: Operation Stopped : Error Domain=AVFoundationErrorDomain Code=-11841 "Operation Stopped" UserInfo=0x1815ca50 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The video could not be composed.}

I have tried first saving the video to another file, but it made no difference.

Here is the code I am using to convert the video:

- (void)convertVideoToLowQuailtyAndFixRotationWithInputURL:(NSURL*)inputURL handler:(void (^)(NSURL *outURL))handler
{
    if ([[inputURL pathExtension] isEqualToString:@"MOV"])
    {
        NSURL *outputURL = [inputURL URLByDeletingPathExtension];
        outputURL = [outputURL URLByAppendingPathExtension:@"mp4"];

        AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:inputURL options:nil];

        AVAssetTrack *sourceVideoTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
        AVAssetTrack *sourceAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];

        AVMutableComposition* composition = [AVMutableComposition composition];

        AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                                    preferredTrackID:kCMPersistentTrackID_Invalid];
        [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration)
                                       ofTrack:sourceVideoTrack
                                        atTime:kCMTimeZero error:nil];
        [compositionVideoTrack setPreferredTransform:sourceVideoTrack.preferredTransform];

        AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
                                                                                    preferredTrackID:kCMPersistentTrackID_Invalid];
        [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration)
                                       ofTrack:sourceAudioTrack
                                        atTime:kCMTimeZero error:nil];

        AVMutableVideoComposition *videoComposition = [self getVideoComposition:avAsset];

        NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
        if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality])
        {
            AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
            exportSession.outputURL = outputURL;
            exportSession.outputFileType = AVFileTypeMPEG4;
            exportSession.shouldOptimizeForNetworkUse = YES;
            exportSession.videoComposition = videoComposition;
            [exportSession exportAsynchronouslyWithCompletionHandler:^{

                switch ([exportSession status])
                {
                    case AVAssetExportSessionStatusFailed:
                        NSLog(@"Export failed: %@ : %@", [[exportSession error] localizedDescription], [exportSession error]);
                        handler(nil);

                        break;
                    case AVAssetExportSessionStatusCancelled:

                        NSLog(@"Export canceled");
                        handler(nil);

                        break;
                    default:

                        handler(outputURL);

                        break;

                }
            }];
        }

    } else {
        handler(inputURL);
    }
}

- (AVMutableVideoComposition *)getVideoComposition:(AVAsset *)asset
{
    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    AVMutableComposition *composition = [AVMutableComposition composition];
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
    CGSize videoSize = videoTrack.naturalSize;
    BOOL isPortrait_ = [self isVideoPortrait:asset];
    if(isPortrait_) {
//        NSLog(@"video is portrait ");
        videoSize = CGSizeMake(videoSize.height, videoSize.width);
    }
    composition.naturalSize     = videoSize;
    videoComposition.renderSize = videoSize;
    videoComposition.frameDuration = CMTimeMakeWithSeconds( 1 / videoTrack.nominalFrameRate, 600);

    AVMutableCompositionTrack *compositionVideoTrack;
    compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil];
    AVMutableVideoCompositionLayerInstruction *layerInst;
    layerInst = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
    [layerInst setTransform:videoTrack.preferredTransform atTime:kCMTimeZero];
    AVMutableVideoCompositionInstruction *inst = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    inst.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
    inst.layerInstructions = [NSArray arrayWithObject:layerInst];
    videoComposition.instructions = [NSArray arrayWithObject:inst];
    return videoComposition;
}
like image 234
Darren Avatar asked Nov 26 '13 14:11

Darren


1 Answers

AVFoundation Error Constant -11841 means that you have an invalid video composition. See this link if you'd like more info on the error constants: https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVFoundation_ErrorConstants/Reference/reference.html

While no major errors pop out at me immediately, I can suggest the following ways to narrow down the source of your problem.

First, instead of passing nil for the error parameter in these calls:

[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration)
                                       ofTrack:sourceVideoTrack
                                        atTime:kCMTimeZero error:nil];

create an NSError object and pass the reference to it like so:

NSError *error = nil;
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration)
                                       ofTrack:sourceVideoTrack
                                        atTime:kCMTimeZero error:&error];

Examine the error to make sure your video and audio tracks get inserted in the composition track correctly. The error should be nil if all goes well.

if(error)
    NSLog(@"Insertion error: %@", error);

You may also want to check your AVAsset's composable and exportable, and hasProtectedContent properties. If these are not YES, YES, and NO, respectively, you may have a problem creating your new video file.

Occasionally I've seen an issue where creating a time range for an audio track does not like the 600 timescale when used in a composition with a video track. You may want to create a new CMTime for the duration (avAsset.duration) in

CMTimeRangeMake(kCMTimeZero, avAsset.duration) 

only for inserting the audio track. In the new CMTime,use a timescale of 44100 (or whatever the sample rate of the audio track is.) The same goes for your videoComposition.frameDuration. Depending on the nominalFrameRate of your video track, your time may not be represented correctly with 600 timescale.

Finally, there's a helpful tool provided by Apple to debug video compositions:

https://developer.apple.com/library/mac/samplecode/AVCompositionDebugViewer/Introduction/Intro.html

It gives a visual representation of your composition and you can see where things don't look like they should.

like image 114
jlw Avatar answered Sep 20 '22 13:09

jlw