AVCaptureVideoPreviewLayer connection crash on iOS5 only [closed] - ios

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I'm using some code from Apple's "SquareCam" source code. It runs fine on iOS6, but on iOS5 I get a crash:
AVCaptureSession *session = [AVCaptureSession new];
[session setSessionPreset:AVCaptureSessionPresetPhoto];
// Select a video device, make an input
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
require( error == nil, bail );
{
isUsingFrontFacingCamera = NO;
if ( [session canAddInput:deviceInput] )
[session addInput:deviceInput];
// Make a still image output
self.stillImageOutput = [AVCaptureStillImageOutput new];
[self.stillImageOutput addObserver:self forKeyPath:#"capturingStillImage" options:NSKeyValueObservingOptionNew context:(__bridge void *)(AVCaptureStillImageIsCapturingStillImageContext)];
if ( [session canAddOutput:self.stillImageOutput] )
[session addOutput:self.stillImageOutput];
// Make a video data output
self.videoDataOutput = [AVCaptureVideoDataOutput new];
// we want BGRA, both CoreGraphics and OpenGL work well with 'BGRA'
NSDictionary *rgbOutputSettings = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:kCMPixelFormat_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
[self.videoDataOutput setVideoSettings:rgbOutputSettings];
[self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; // discard if the data output queue is blocked (as we process the still image)
// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
[self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
if ( [session canAddOutput:self.videoDataOutput] )
[session addOutput:self.videoDataOutput];
[[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:NO];
effectiveScale = 1.0;
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
//[self.previewLayer setBackgroundColor:[[UIColor redColor] CGColor]];
//self.previewLayer.orientation = UIInterfaceOrientationLandscapeLeft;
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
//Get Preview Layer connection - this is for orientation purposes
AVCaptureConnection *previewLayerConnection=self.previewLayer.connection; //THIS CRASHES ON IOS5
if ([previewLayerConnection isVideoOrientationSupported])
[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
CALayer *rootLayer = [self.previewView layer];
[rootLayer setMasksToBounds:YES];
[self.previewLayer setFrame:self.view.bounds];
[rootLayer addSublayer:self.previewLayer];
[session startRunning];
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
This is the line it crashes on:
AVCaptureConnection *previewLayerConnection=self.previewLayer.connection;
This is the error message:
-[AVCaptureVideoPreviewLayer connection]: unrecognized selector sent to instance
I don't really understand AVCapture that much in the first place. I'm just trying to take a picture. But why would this work fine on iOS6 but not iOS5?

The error message says:
-[AVCaptureVideoPreviewLayer connection]: unrecognized selector sent to instance
So it's telling you that you can't say connection to an AVCaptureVideoPreviewLayer.
And indeed, the docs on AVCaptureVideoPreviewLayer say:
connection
Available in iOS 6.0 and later.
So there's the reason: in iOS 5 there's no connection property of an AVCaptureVideoPreviewLayer.

Related

How to set scanning bound using AVFoundation

I am trying barcode scanner in one of my iOS application. I had successfully implemented the barcode scanner.
But currently barcode scanning is displayed in full screen only. But what I want is that, the video should be viewed in full screen and the barcode should be scanned in particular portion only. That is, if the barcode is placed in that portion then only it should be displayed. Below is my current code:
session=[[AVCaptureSession alloc]init];
device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error=nil;
input=[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (input) {
[session addInput:input];
}
else{
NSLog(#"Errod : %#",error);
}
output=[[AVCaptureMetadataOutput alloc]init];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[session addOutput:output];
output.metadataObjectTypes=[output availableMetadataObjectTypes];
prevLayer=[AVCaptureVideoPreviewLayer layerWithSession:session];
[prevLayer setFrame:self.view.bounds];
[prevLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[self.view.layer addSublayer:prevLayer];
[session startRunning];
[self.view bringSubviewToFront:self.lblCode];
[self.view bringSubviewToFront:self.imgShade1];
[self.view bringSubviewToFront:self.imgShade2];
This is what you are after:
CGRect visibleMetadataOutputRect = [prevLayer metadataOutputRectOfInterestForRect:areaOfInterest];
output.rectOfInterest = visibleMetadataOutputRect;
where areaOfInterest is a CGRect. Hope this solves the issue.
Maybe it's late to answer this question, but I've just overcome this issue myself. So, hope to help others later.
The keyword is rectOfInterest of AVCaptureMetadataOutput, and here's how I set mine.
CGSize size = self.view.bounds.size;
_output.rectOfInterest = CGRectMake(cropRect.origin.y/size.height, cropRect.origin.x/size.width, cropRect.size.height/size.height, cropRect.size.width/size.width);
For more detail, you can check Document from Apple Inc.
Good Luck. :)

Keep getting "inactive/invalid connection passed" error when trying to capture still image using AV Foundation

I have a View Controller that is using AV Foundation. As soon as the View controller loads, the user is able to see exactly what the input device is seeing. This is because I have started the AVCaptureSession in the viewDidLoad method implementation.
Here is the code that I have in viewDidLoad:
[super viewDidLoad];
AVCaptureSession *session =[[AVCaptureSession alloc]init];
[session setSessionPreset:AVCaptureSessionPresetHigh];
AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = [[NSError alloc]init];
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];
if([session canAddInput:deviceInput])
[session addInput:deviceInput];
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:session];
[previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
CALayer *rootLayer = [[self view]layer];
[rootLayer setMasksToBounds:YES];
[previewLayer setFrame:CGRectMake(0, 0, rootLayer.bounds.size.width, rootLayer.bounds.size.height/2)];
[rootLayer insertSublayer:previewLayer atIndex:0];
[session startRunning];
And then I have an IBAction method implementation that has been connected to a UIButton for this view controller. Here is the IBAction implementation's code:
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc]init];
AVCaptureConnection *connection = [[AVCaptureConnection alloc]init];
[stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSLog(#"Image Data Captured: %#", imageDataSampleBuffer);
NSLog(#"Any errors? %#", error);
if(imageDataSampleBuffer) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc]initWithData:imageData];
NSLog(#"%#", image);
}
}];
When I run the app on my iPhone and press the button connected to this implementation, I get this error in the console:
*** -[AVCaptureStillImageOutput captureStillImageAsynchronouslyFromConnection:completionHandler:] - inactive/invalid connection passed.'
I looked in the xcode docs and it does say "You can only add an AVCaptureConnection instance to a session using addConnection: if canAddConnection: returns YES", but I have tried doing the method call on my AVCaptureSession object for addConnection and canAddConnection but they don't even show up as available options.
I also read somewhere else that for iOS you don't have to manually create a connection, but this doesn't make sense to me because in my IBAction's code there is a method call of: captureStillImageAsynchronouslyFromConnection: which requires an input.
So if the connection is automatically created for you, what is it called so I can use it for the input?
This is my first time working with AV Foundation and I just can't seem to figure out this connection error.
Any help is greatly appreciated.
When you set up camera, add a stillImageOutput to your AVCaptureSession.
self.stillImageOutput = AVCaptureStillImageOutput()
let stillSettings = [AVVideoCodecJPEG:AVVideoCodecKey]
self.stillImageOutput.outputSettings = stillSettings
if(self.session.canAddOutput(self.stillImageOutput)){
self.session.addOutput(self.stillImageOutput)
}
Then when taking photo, get the AVCaptureSession from stillImageOutput.
func takePhoto(sender: UIButton!){
let connection = self.stillImageOutput.connectionWithMediaType(AVMediaTypeVideo)
if(connection.enabled){
self.stillImageOutput.captureStillImageAsynchronouslyFromConnection(connection, completionHandler: {(buffer: CMSampleBuffer!, error: NSError!) -> Void in
println("picture taken")//this never gets executed
})
}
}
Did you have a property to retain the AVCaptureSession, like
#property (nonatomic, strong) AVCaptureSession *captureSession;
//...
self.captureSession = session;
I hope it helps you.

Camera feed slow to load with AVCaptureSession on iOS - How can I speed it up?

Right now I'm trying to allow users to take pictures in my app without using UIImagePickerController. I'm using AVCaptureSession and all the related classes to load a camera feed as a sublayer on a full-screen view I have on one of my view controllers. The code works but unfortunately the camera is very slow to load. Usually takes 2-3 seconds. Here is my code:
session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;
if ([session canSetSessionPreset:AVCaptureSessionPresetHigh])
//Check size based configs are supported before setting them
[session setSessionPreset:AVCaptureSessionPresetHigh];
[session setSessionPreset:AVCaptureSessionPreset1280x720];
CALayer *viewLayer = self.liveCameraFeed.layer;
//NSLog(#"viewLayer = %#", viewLayer);
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
captureVideoPreviewLayer.frame = viewLayer.bounds;
[viewLayer addSublayer:captureVideoPreviewLayer];
AVCaptureDevice *device;
if(isFront)
{
device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
else
{
device = [self frontCamera];
}
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput * audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
[session addInput:audioInput];
NSError *error = nil;
input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
//NSLog(#"ERROR: trying to open camera: %#", error);
}
[session addInput:input];
[session startRunning];
stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[stillImageOutput setOutputSettings:outputSettings];
[session addOutput:stillImageOutput];
Is there any way to speed it up? I've already tried loading it on another thread using Grand Central Dispatch and NSThread and though that stopped the app from freezing it made the loading of the camera take even longer. Any help is appreciated.
In my case, I need to wait for session to start running
dispatch_async(queue) {
self.session.startRunning()
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.cameraManDidStart(self)
let layer = AVCaptureVideoPreviewLayer(session: self.session)
}
}
Waiting for AVCaptureSession's startRunning function was my solution too. You can run startRunning in global async and then in main thread you can add your AVCaptureVideoPreviewLayer.
Swift 4 sample
DispatchQueue.global().async {
self.captureSession.startRunning()
DispatchQueue.main.async {
let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
}
}
You can load the AVCaptureSession at the time of viewWillAppear. It works for me. When I switch to the view with the AVCaptureSession from other view, then I see the camera running immediately.
For anyone interested the solution I came up with was preloading the camera on a different thread and keeping it open.
I tried all the above methods but it was not as good as Instagram or Facebook, So I loaded the AVCaptureDevice, AVCaptureVideoPreviewLayer, AVCaptureSession in the Parent Screen and passed it as parameter to the Child Screen. It was loading very rapidly.

AVCaptureOutput captureStillImageAsynchronouslyFromConnection never completes on iPhone5

In my application I am displaying a AVCaptureVideoPreviewLayer and then capturing a still image when the user clicks a button using the captureStillImageAsynchronouslyFromConnection function in AVCaptureOutput. This has worked well for me up until the iPhone 5, on which it never completes.
My setup code is:
...
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[self.imageOutput setOutputSettings:outputSettings];
self.captureSession = [[[AVCaptureSession alloc] init] autorelease];
[self.captureSession addInput:self.rearFacingDeviceInput];
[self.captureSession addOutput:self.imageOutput];
[self.captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
self.previewLayer.frame = CGRectMake(0, 0, 320, 427);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.captureSession startRunning];
[outputSettings release];
My capture method is:
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in self.imageOutput.connections){
for (AVCaptureInputPort *port in [connection inputPorts]){
if ([[port mediaType] isEqual:AVMediaTypeVideo] ){
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
//code to abort if not return 'soon'
...
[self.imageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error){
//use image here
}];
captureStillImageAsynchronouslyFromConnection never completes for me using an iPhone5
I have tested:
It isn't OS 6 as this code works on both an iPhone 4s and an iPod (iPod touch (4th generation) that have been updated
The captureSession is running
videoConnection is not nil
imageOutput is not nil
Also:
I'm using this method and not UIImagePickerController because I need to put the preview as a subview.
calling stopRunning on the capture Session takes several seconds on the iPhone 5 as well
Well, this code works fine. Tested for both iPhone 4 and 5, (baseSDK 7.1, under ARC).
Few things you have to consider.
1) be sure you set rearFacingDeviceInput properly,
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self setRearFacingDeviceInput:[AVCaptureDeviceInput deviceInputWithDevice:device error:nil]];
2) as Vincent mentioned, there will be an error, try to log both an error and imageSampleBuffer
3) session's -startRunning and -stopRunning operations takes long time to complete (seconds, even 5-6s), those methods doesn't return until they are done all the work, to avoid blocked UI you shouldn't call these methods on main thread, one way is using GCD
dispatch_queue_t serialQueue = dispatch_queue_create("queue", NULL);
dispatch_async(serialQueue, ^{
[self.captureSession startRunning];
});
If still captureStillImageAsynchronously doesn't complete (to ensure that, add breakpoint in block, and log everything) you should check your device's camera. I believe your code works on all iPhone 5 devices. Hope this helps, good luck.

I want to throttle video capture frame rate in AVCapture framework

I am trying to throttle my video capture framerate for my application, as I have found that it is impacting VoiceOver performance.
At the moment, it captures frames from the video camera, and then processes the frames using OpenGL routines as quickly as possible. I would like to set a specific framerate in the capture process.
I was expecting to be able to do this by using videoMinFrameDuration or minFrameDuration, but this seems to make no difference to performance. Any ideas?
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices)
{
if ([device position] == AVCaptureDevicePositionBack)
{
backFacingCamera = device;
// SET SOME OTHER PROPERTIES
}
}
// Create the capture session
captureSession = [[AVCaptureSession alloc] init];
// Add the video input
NSError *error = nil;
videoInput = [[[AVCaptureDeviceInput alloc] initWithDevice:backFacingCamera error:&error] autorelease];
// Add the video frame output
videoOutput = [[AVCaptureVideoDataOutput alloc] init];
[videoOutput setAlwaysDiscardsLateVideoFrames:YES];
[videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
[videoOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
// Start capturing
if([backFacingCamera supportsAVCaptureSessionPreset:AVCaptureSessionPreset1920x1080])
{
[captureSession setSessionPreset:AVCaptureSessionPreset1920x1080];
captureDeviceWidth = 1920;
captureDeviceHeight = 1080;
#if defined(VA_DEBUG)
NSLog(#"Video AVCaptureSessionPreset1920x1080");
#endif
}
else do some fall back stuff
// If you wish to cap the frame rate to a known value, such as 15 fps, set
// minFrameDuration.
AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
if (conn.supportsVideoMinFrameDuration)
conn.videoMinFrameDuration = CMTimeMake(1,2);
else
videoOutput.minFrameDuration = CMTimeMake(1,2);
if ([captureSession canAddInput:videoInput])
[captureSession addInput:videoInput];
if ([captureSession canAddOutput:videoOutput])
[captureSession addOutput:videoOutput];
if (![captureSession isRunning])
[captureSession startRunning];
Any ideas? Am I missing something? Is this the best way to throttle?
AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
if (conn.supportsVideoMinFrameDuration)
conn.videoMinFrameDuration = CMTimeMake(1,2);
else
videoOutput.minFrameDuration = CMTimeMake(1,2);
Mike Ullrich's answer worked up until ios 7. These two methods are unfortunately deprecated in ios7. You have to set the activeVideo{Min|Max}FrameDuration on the AVCaptureDevice itself. Something like:
int fps = 30; // Change this value
AVCaptureDevice *device = ...; // Get the active capture device
[device lockForConfiguration:nil];
[device setActiveVideoMinFrameDuration:CMTimeMake(1, fps)];
[device setActiveVideoMaxFrameDuration:CMTimeMake(1, fps)];
[device unlockForConfiguration];
Turns out you need to set both videoMinFrameDuration and videoMaxFrameDuration for either one to work.
eg:
[conn setVideoMinFrameDuration:CMTimeMake(1,1)];
[conn setVideoMaxFrameDuration:CMTimeMake(1,1)];

Resources