Reputation: 14722
I am attempting to implement a barcode scanner. I have an AVCaptureSession that takes in video from AVCaptureDevice. I want to support all orientations. With the following code, when I run the app, everything is fine in the portrait orientation. However in landscape orientation, the view rotates but the video input does not. So I end up with a 90degree rotated video.
When I implement -(NSUInteger)supportedInterfaceOrientations method, then everything gets locked to the portrait position.
Can anyone tell me how I can fix this issue?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setupCaptureSession];
_previewLayer.frame = _previewView.bounds;
_previewView.center = self.view.center;
_previewView.backgroundColor = [UIColor redColor];
[_previewView.layer addSublayer:_previewLayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self startRunning];
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self stopRunning];
}
-(void) setupCaptureSession
{
if (_captureSession)
return;
_videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (!_videoDevice)
{
NSLog(@"No video camera on this device");
return;
}
_captureSession = [[AVCaptureSession alloc]init];
_videoInput = [[AVCaptureDeviceInput alloc]initWithDevice:_videoDevice error:nil];
if ([_captureSession canAddInput:_videoInput])
{
[_captureSession addInput:_videoInput];
}
_previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
}
-(void) startRunning
{
if (_running)
return;
[_captureSession startRunning];
_running = YES;
}
- (void) stopRunning
{
if (!_running)
return;
[_captureSession stopRunning];
_running = NO;
}
/*
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationLandscapeLeft|UIInterfaceOrientationLandscapeRight|UIInterfaceOrientationPortrait|UIInterfaceOrientationPortraitUpsideDown;
}
*/
Edit:
I tried the following code but the previewLayer orientation is still upsidedown/sideways.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(orientationChanged)
name:UIDeviceOrientationDidChangeNotification
object:nil];
-(void) orientationChanged
{
if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
if (self.interfaceOrientation == UIInterfaceOrientationPortrait)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
}
Upvotes: 3
Views: 7191
Reputation: 14722
Ah fixed it with the following
-(void) orientationChanged {
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
else if (deviceOrientation == UIInterfaceOrientationPortrait)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
else
[_previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
}
Upvotes: 11
Reputation: 7560
I don't have new informations on this issue, but I think both provided answers are good and still just the half story. I worked on this issue quite a long time today and didn't find the right answer for my situation. But almost.
Actually you'd need to comply with both solutions. You definitely need to rotate the preview layer correspondingly. But if you want to have the footage in the same orientation (and not somehow cut off), you'd need to do a combination.
Also I'm including a fix: -[UIDevice orientation]
gets you with the real orientation of the device in the room. But you'd probably better rely on the interface orientation of the application as seen in the code below.
In my case I only support landscape (both) and have a video recorder which writes to disk. Here I'm using a block for the notifications, since I like 'em a lot ;)
// @property (nonatomic, retain) AVCaptureMovieFileOutput *videoFileOutput;
// @property (nonatomic, retain) AVCaptureVideoPreviewLayer *videoPreviewLayer;
- (void)viewDidLoad {
[super viewDidLoad];
void (^observerHandler)(NSNotification *) = ^(NSNotification *note) {
switch ([[UIApplication sharedApplication] statusBarOrientation]) {
case UIInterfaceOrientationLandscapeRight:
[[[self videoPreviewLayer] connection] setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
[[[self videoFileOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
break;
case UIInterfaceOrientationLandscapeLeft:
[[[self videoPreviewLayer] connection] setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
[[[self videoFileOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
break;
default:
break;
}
};
[self setOrientationObserver:[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:observerHandler]];
}
Upvotes: 0
Reputation: 5386
To reflect device orientation changes in your captured media you need to set the videoOrientation
property of a output capture connection.
AVCaptureOutput* output = <# Your capture output device #>; //reference to your device output, possibly of AVCaptureStillImageOutput or AVCaptureMovieFileOutput type
AVCaptureConnection* connection = [output connectionWithMediaType:AVMediaTypeVideo];
if ([connection isVideoOrientationSupported]) {
connection.videoOrientation = [self videoOrientationFromDeviceOrientation];
}
And the method below returns video orientation based on device orientation:
-(AVCaptureVideoOrientation)videoOrientationFromDeviceOrientation {
AVCaptureVideoOrientation result = [UIDevice currentDevice].orientation;
if ( result == UIDeviceOrientationLandscapeLeft )
result = AVCaptureVideoOrientationLandscapeRight;
else if ( result == UIDeviceOrientationLandscapeRight )
result = AVCaptureVideoOrientationLandscapeLeft;
return result;
}
Hope you would find it helpful.
Upvotes: 8