how to control orientation of video assembled with AVMutableComposition
I am assembling a bunch of video clips filmed on the iPhone in portrait mode. To assemble them I am taking straightforward approach as follows:
AVURLAsset to get hold of the different videos then shoving these into an AVMutableCompositionTrack and then putting this into an AVMutableComposition which I'm exporting to file with AVAssetExportSession
My problem is that when I come to display the video in a UIWebView, it is appearing in landscape mode. However, if I view any of the component views they appear in Portrait view. Does anyone know how to sort out the orientation. I tried messing around with the AVMutableComposition naturalSize changing the width and height around but that just made my people look sh开发者_如何学编程ort and fat! (whilst on their side)
Thanks in advance for any thoughts / suggestions
Tudor
If all you want is preserve you video orientation you may want to assign AVMutableCompositionTrack prefferedTransition value from AVAssetTrack like this:
AVAssetTrack *videoAssetTrack= [[videoAsset tracksWithMediaType:AVMediaTypeVideo] lastObject];
AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error];
videoCompositionTrack.preferredTransform = videoAssetTrack.preferredTransform;
Setup a video composition that will handle rotate + scale:
AVMutableVideoComposition* videoComposition = [[AVMutableVideoComposition videoComposition]retain];
videoComposition.renderSize = CGSizeMake(320, 240);
videoComposition.frameDuration = CMTimeMake(1, 30);
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30) );
AVMutableVideoCompositionLayerInstruction* rotator = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]];
CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation( 0,-320);
CGAffineTransform rotateBy90Degrees = CGAffineTransformMakeRotation( M_PI_2);
CGAffineTransform shrinkWidth = CGAffineTransformMakeScale(0.66, 1); // needed because Apple does a "stretch" by default - really, we should find and undo apple's stretch - I suspect it'll be a CALayer defaultTransform, or UIView property causing this
CGAffineTransform finalTransform = CGAffineTransformConcat( shrinkWidth, CGAffineTransformConcat(translateToCenter, rotateBy90Degrees) );
[rotator setTransform:finalTransform atTime:kCMTimeZero];
instruction.layerInstructions = [NSArray arrayWithObject: rotator];
videoComposition.instructions = [NSArray arrayWithObject: instruction];
Here's some code that adjusted the rotation using the preferred transform:
// our composition, with one video and one audio track
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// loop through all the clips, adding them to our track and inserting composition instructions
NSDictionary* options = @{ AVURLAssetPreferPreciseDurationAndTimingKey: @YES };
CMTime insertionPoint = kCMTimeZero;
NSMutableArray* instructions = [NSMutableArray array];
NSError* error = nil;
for (NSURL* outputFileURL in self.movieFileURLs) {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:outputFileURL options:options];
// insert this clip's video track into our composition
NSArray *videoAssetTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoAssetTrack = (videoAssetTracks.count > 0 ? [videoAssetTracks objectAtIndex:0] : nil);
[videoTrack insertTimeRange:CMTimeRangeFromTimeToTime(kCMTimeZero, asset.duration) ofTrack:videoAssetTrack atTime:insertionPoint error:&error];
// create a layer instruction at the start of this clip to apply the preferred transform to correct orientation issues
AVMutableVideoCompositionLayerInstruction *instruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoAssetTrack];
[instruction setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];
// create the composition instructions for the range of this clip
AVMutableVideoCompositionInstruction * videoTrackInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoTrackInstruction.timeRange = CMTimeRangeMake(insertionPoint, asset.duration);
videoTrackInstruction.layerInstructions = @[instruction];
[instructions addObject:videoTrackInstruction];
// insert this clip's audio track into our composition
NSArray *audioAssetTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *audioAssetTrack = (audioAssetTracks.count > 0 ? [audioAssetTracks objectAtIndex:0] : nil);
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:audioAssetTrack atTime:insertionPoint error:nil];
// advance the insertion point to the end of the clip
insertionPoint = CMTimeAdd(insertionPoint, asset.duration);
}
// create our video composition which will be assigned to the player item
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.instructions = instructions;
videoComposition.frameDuration = CMTimeMake(1, videoTrack.naturalTimeScale);
videoComposition.renderSize = videoTrack.naturalSize;
_videoComposition = videoComposition;
精彩评论