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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With