I'm trying to disable any discernable orientation rotation to an AVCaptureVideoPreviewLayer while still maintaining rotation for any subviews. AVCaptureVideoPreviewLayer does have an orientation property, and changing it does allow for the layer to display properly for any orientation. However, the rotation involves some funky rotation of the AVCaptureVideoPreviewLayer, rather than staying smooth as it does in the Camera app.
This is how I've gotten orientation to work properly, minus the hitch in the rotation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
_captureVideoPreviewLayer.frame = self.view.bounds;
_captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation];
}
How do I get this layer to act like the Camera app, while maintaining rotations for its subviews?
Also, as an aside, I've seen that the orientation property is depreciated on AVCaptureVideoPreviewLayer, and the videoOrientation property of AVCaptureConnection should be used instead, but I don't think I have an AVCaptureConnection to access here (I'm simply displaying the camera on the screen). How should I set this up to access videoOrientation?
Using an affine transform on the view containing the preview layer creates a smooth transition:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (cameraPreview) {
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
cameraPreview.transform = CGAffineTransformMakeRotation(0);
} else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
cameraPreview.transform = CGAffineTransformMakeRotation(M_PI/2);
} else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
cameraPreview.transform = CGAffineTransformMakeRotation(-M_PI/2);
}
cameraPreview.frame = self.view.bounds;
}
}
The willAnimateRotationToInterfaceOrientation
function is called within the rotation animation block so changing properties here will animate along with the rest of the rotation animations. This function has been phased out in iOS 6 and replaced with new methods to handle device rotation, so this works for now, but isn't the best solution.
This is an old question, but since it didn't have an accepted answer and I've been dealing with this issue myself for the past several days, I figured I would post the answer.
The trick to rotating the video based on device orientation changes, is to NOT ROTATE the AVCaptureVideoPreviewLayer
or the AVCaptureConnection
at all.
Changing the orientation on the AVCaptureConnection
( AVCaptureVideoPreviewLayer
's orientation was deprecated ) ALWAYS results in an ugly animated change .. And after the video is rotated, you would still have to transform the preview layer.
The correct way to do this is use an AVAssetWriter
to write the Video and Audio data .. and then apply a transform on the AVAssetWriterInput
at the time that you start recording.
Here is a blurb from Apple about this --
Receiving rotated CVPixelBuffers from AVCaptureVideoDataOutput
To request buffer rotation, a client calls -setVideoOrientation: on the AVCaptureVideoDataOutput's video AVCaptureConnection. Note that physically rotating buffers does come with a performance cost, so only request rotation if it's necessary. If, for instance, you want rotated video written to a QuickTime movie file using AVAssetWriter, it is preferable to set the -transform property on the AVAssetWriterInput rather than physically rotate the buffers in AVCaptureVideoDataOutput.
The applying of the transform is similar to the code posted above.
Hope this helps whoever is looking for an answer.
I was able to accomplish this with the following code:
Set up when you need the camera:
preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain];
[self.view addSubview:preview];
The videoPreviewWithFrame
function.
- (UIView *) videoPreviewWithFrame:(CGRect) frame {
AVCaptureVideoPreviewLayer *tempPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]];
[tempPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
tempPreviewLayer.frame = frame;
UIView* tempView = [[UIView alloc] init];
[tempView.layer addSublayer:tempPreviewLayer];
tempView.frame = frame;
[tempPreviewLayer autorelease];
[tempView autorelease];
return tempView;
}
I wanted the AVCaptureVideoPreviewLayer to handsomely follow all rotations. This is how I did it (overlayView
is just a view that happens to have the correct bounds). It only animates without a hick-up when you place your calls to super exactly where they are in the code:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if ([[[self previewLayer] connection] isVideoOrientationSupported])
{
[[[self previewLayer] connection] setVideoOrientation:toInterfaceOrientation];
}
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
CGRect layerRect = [[self overlayView] bounds];
[[self previewLayer] setBounds:layerRect];
[[self previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),
CGRectGetMidY(layerRect))];
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration: duration];
}
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