AVFoundation - why can't I get the video orientation right
avfoundation, objective-c
Solution
The problem is that modifying the `writerInput.transform` property only adds a tag in the video file metadata which instructs the video player to rotate the file during playback. That's why the videos play in the correct orientation on your device (I'm guessing they also play correctly in a Quicktime player as well).
The pixel buffers captured by the camera are still laid out in the orientation in which they were captured. Many video players will not check for the preferred orientation metadata tag and will just play the file in the native pixel orientation.
If you want the user to be able to record video holding the phone in either landscape mode, you need to rectify this at the `AVCaptureSession` level before compression by performing a transform on the CVPixelBuffer of each video frame. This Apple Q&A covers it (look at the `AVCaptureVideoOutput` documentation as well): https://developer.apple.com/library/ios/qa/qa1744/_index.html
Investigating the link above is the correct way to solve your problem. An alternate fast n' dirty way to solve the same problem would be to lock the recording UI of your app into only one landscape orientation and then to rotate all of your videos server-side using ffmpeg.
Problem
I am using `AVCaptureSession` to capture video from a devices camera and then using `AVAssetWriterInput` and `AVAssetTrack` to compress/resize the video before uploading it to a server. The final videos will be viewed on the web via an html5 video element. I'm running into multiple issues trying to get the orientation of the video correct. My app only supports landscape orientation and all captured videos should be in landscape orientation. However, I would like to allow the user to hold their device in either landscape direction (i.e. home button on either the left or the right hand side). I am able to make the video preview show in the correct orientation with the following line of code ``` _previewLayer.connection.videoOrientation = UIDevice.currentDevice.orientation; ``` The problems start when processing the video via `AVAssetWriterInput` and friends. The result does not seem to account for the left vs. right landscape mode the video was captured in. IOW, sometimes the video comes out upside down. After some googling I found many people suggesting that the following line of code would solve this issue ``` writerInput.transform = videoTrack.preferredTransform; ``` ...but this doesn't seem to work. After a bit of debugging I found that `videoTrack.preferredTransform` is always the same value, regardless of the orientation the video was captured in. I tried manually tracking what orientation the video was captured in and setting the `writerInput.transform` to `CGAffineTransformMakeRotation(M_PI)` as needed. Which solved the problem!!! ...sorta When I viewed the results on the device this solution worked as expected. Videos were right-side-up regardless of left vs. right orientation while recording. Unfortunately, when I viewed the exact same videos in another browser (chrome on a mac book) they were all upside-down!?!?!? What am I doing wrong? EDIT Here's some code, in case it's helpful... ``` -(void)compressFile:(NSURL*)inUrl; { NSString* fileName = [@"compressed." stringByAppendingString:inUrl.lastPathComponent]; NSError* error; NSURL* outUrl = [PlatformHelper getFilePath:fileName error:&error]; NSDictionary* compressionSettings = @{ AVVideoProfileLevelKey: AVVideoProfileLevelH264Main31, AVVideoAverageBitRateKey: [NSNumber numberWithInt:2500000], AVVideoMaxKeyFrameIntervalKey: [NSNumber numberWithInt: 30] }; NSDictionary* videoSettings = @{ AVVideoCodecKey: AVVideoCodecH264, AVVideoWidthKey: [NSNumber numberWithInt:1280], AVVideoHeightKey: [NSNumber numberWithInt:720], AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill, AVVideoCompressionPropertiesKey: compressionSettings }; NSDictionary* videoOptions = @{ (id)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] }; AVAssetWriterInput* writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; writerInput.expectsMediaDataInRealTime = YES; AVAssetWriter* assetWriter = [AVAssetWriter assetWriterWithURL:outUrl fileType:AVFileTypeMPEG4 error:&error]; assetWriter.shouldOptimizeForNetworkUse = YES; [assetWriter addInput:writerInput]; AVURLAsset* asset = [AVURLAsset URLAssetWithURL:inUrl options:nil]; AVAssetTrack* videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; // !!! this line does not work as expected and causes all sorts of issues (videos display sideways in some cases) !!! //writerInput.transform = videoTrack.preferredTransform; AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:videoOptions]; AVAssetReader* assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error]; [assetReader addOutput:readerOutput]; [assetWriter startWriting]; [assetWriter startSessionAtSourceTime:kCMTimeZero]; [assetReader startReading]; [writerInput requestMediaDataWhenReadyOnQueue:_processingQueue usingBlock: ^{ /* snip */ }]; } ```