Rotating AV video preview area and other elements when using AV Foundation
I had started a project using UIImagePickerController to capture still images. I had abandoned using this simple mechanism because of the poor interaction between overlays and just-captured images. The problem was as follows: If you have an overlay active, and snap a photo, the just snapped image rotates around to do an aspect fill in the portrait orientation, when the user is presented the option to use or retake the image. When this occurs, there is no way to dismiss the overlay, or rotate the overlay to correspond to the new still image preview orientation. Not good.
I decided to attempt to re-implement the still image capture behavior of UIImagePickerController using the various mechanisms from AV Foundation. This has been surprisingly easy to set up, but I still have some lingering issues:
1) I want to re-orienting mechanism to be similar to how the UI works in the standard UIImagePickerController. In this controller, as you know, the button bar always stays fixed to the bottom of the portrait orientation, regardless of how the phone is rotated. Within this button bar, the glyphs in the buttons themselves rotate, but everything else remains fixed. The overlay controls, for flash set, camera toggle, and options, does reorient. How can I get this tab bar to remain pinned while rotating these other elements? Is there a way to exclude certain elements from rotation?
2) Upon doing the automatic reorientation, I want the AVVideoCaptureLayer to always remain live and occupy the entire screen. When the device performs the reorientation rotation, this layer gets sized strangely (shrunk down) and the video feed itself is off by 90 degrees. So apparently this layer is being rotated but not to the full screen dimension, and the video feed itself isn't behaving as expected. I want the video to remain fixed and occupying the full screen through the full rotation, with up always being up, and no resizing of the preview during rotation. But I also want to be able to do as in item 1, with certain elements reorienting.
Any sample code illustrating how to achi开发者_C百科eve this is greatly appreciated.
just found out that using the orientation property on AVCaptureVideoPreviewLayer is deprecated. here is an updated solution (although the previous solution shared by @Jaret Ward was much less work :) :
use the recommend approach of changing it with setVideoOrientation in AVCaptureConnection. I would add a call to this in viewDidAppear and in one of your rotation callbacks.
//setup a connection to the preview layer
AVCaptureConnection *connection;
connection = [self.videoPreviewLayer connection];
[connection setVideoOrientation:[self avOrientationForDeviceOrientation:[[UIDevice currentDevice] orientation]]];
//translate the orientation
- (AVCaptureVideoOrientation)avOrientationForDeviceOrientation:(UIDeviceOrientation)deviceOrientation {
AVCaptureVideoOrientation result = deviceOrientation;
if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
result = AVCaptureVideoOrientationLandscapeRight;
else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
result = AVCaptureVideoOrientationLandscapeLeft;
else if( deviceOrientation == UIDeviceOrientationPortrait)
result = AVCaptureVideoOrientationPortrait;
else if( deviceOrientation == UIDeviceOrientationPortraitUpsideDown)
result = AVCaptureVideoOrientationPortraitUpsideDown;
return result;
}
By setting the orientation property on the video preview layer's connection.
This is just a cleaner version of @CocoaEv's answer. It uses the newer dot syntax in addition to making the orientation fix a function instead of a method.
self.videoPreviewLayer.connection.videoOrientation = AVOrientationFromDeviceOrientation([UIDevice currentDevice].orientation);
AVCaptureVideoOrientation AVOrientationFromDeviceOrientation(UIDeviceOrientation deviceOrientation) {
if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
return AVCaptureVideoOrientationLandscapeRight;
else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
return AVCaptureVideoOrientationLandscapeLeft;
else if( deviceOrientation == UIDeviceOrientationPortrait)
return AVCaptureVideoOrientationPortrait;
else if( deviceOrientation == UIDeviceOrientationPortraitUpsideDown)
return AVCaptureVideoOrientationPortraitUpsideDown;
else
return deviceOrientation;
}
I encountered a similar issue in my own efforts and finally let go of the idea of trying to affect the video stream. Instead, I focused on the AVCaptureVideoPreviewLayer itself and came up with this solution:
- (void)orientationChangedMethod {
self.previewLayer.orientation = [[UIDevice currentDevice] orientation];
self.previewLayer.frame = self.view.bounds;
}
Where orientationChangedMethod is a fill in to the (very helpful) code at the.ichibod.com and self.previewLayer is a reference to my AVCaptureVideoPreviewLayer instance.
This answer is deprecated since iOS 6!
精彩评论