Showing posts with label AVFoundation. Show all posts
Showing posts with label AVFoundation. Show all posts

Saturday, 20 August 2016

QRCode Scanner

One of the great feature of AVFoundation framework is the ability of discover  and read bar Codes in real time.
Here i am mainly going to interact (discover and read ) with QR code.

QR(Quick Response) code is a type of matrix barcode or two dimensional barcode uses four standardized encoding mode (numeric, alphanumeric, byte/binary and kanji) to store data (like URLs, Text, Numbers). For more details Click here

For more details about AVFoundation Framework Click here.

Create a new Project
import AVFoundation Framework.

Now go to main storyboard and add a view(Subclass of UIView), a label and a button.

A view will be  display what camera is recording in the form of image.
Label is used for display resultant string data after scanning  QRcode (or the data store in QR code).
And button is for start and stop scanning.

 Now go to ViewController.h file and declare properties.

@property (nonatomic, weak) IBOutlet UIView *previewView;
@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
@property (weak, nonatomic) IBOutlet UIButton *scanButton;


also declare two more properties
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;


Back to main storyboard and connect outlets.

Switch to ViewController.m file and add following code inside viewDidLoad method

   //set button corner radious and border color
    self.scanButton.layer.borderColor = [UIColor lightGrayColor].CGColor;
    self.scanButton.layer.borderWidth = 0.7;
    self.scanButton.layer.cornerRadius = 3;
    self.scanButton.clipsToBounds = YES;


    //setting lable color and border
    self.statusLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
    self.statusLabel.layer.borderWidth = 0.7;
    self.statusLabel.layer.cornerRadius = 2;
    self.statusLabel.clipsToBounds = YES;


In the above code we are just adding border color and corner radius on label and button.

Add new method with the name startReading()

-(Void) startReading{
   
    NSError *error;
    

//1. Define capture device
    //For Front Camera
//    AVCaptureDevice *captureDevice = [AVCaptureDevice deviceWithUniqueID:@"com.apple.avfoundation.avcapturedevice.built-in_video:1"];
   
    //For Default Camera
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    

//2.Define Capture Device
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (!input) {
        NSLog(@"Error = %@", error.localizedDescription);
        NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey];
        [[[UIAlertView alloc] initWithTitle:@"Camera Unavailable" message:[NSString stringWithFormat:@"The %@ has not been given a permission to your camera. Please check the Privacy Settings: Settings -> %@ -> Privacy -> Camera", appName, appName] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
        return NO;
    }
    //3.Initialize session
    self.captureSession = [[AVCaptureSession alloc] init];
    [self.captureSession addInput:input]; //add input device to session   
 

 //4.Set metadata output
    AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.captureSession addOutput:captureMetadataOutput];// Add output data to session
   
    //5. set delegate
    dispatch_queue_t dispatchQueue;
    dispatchQueue = dispatch_queue_create("myQueue", NULL);
    [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue]; //set delegate 
 

 //6.set the type of metadata objects to process
     [captureMetadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
    

//7.Add video preview layer
    self.videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
    [self.videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    self.videoPreviewLayer.frame = self.previewView.bounds;
    [self.previewView.layer addSublayer:self.videoPreviewLayer];
 

//8.Start session  
 [self.captureSession startRunning];
}


1.Capture Device: 

An AVCaptureDevice represents a physical device that provides realtime input media data, such as video and audio. A input device can be the camera or microphone.

2.Capture Device Input:

AVCaptureDeviceInput is a concrete subclass of AVCaptureInput that provides an interface for capturing media from an AVCaptureDevice.

3. Capture Session:
AVCaptureSession is the central hub of the AVFoundation capture classes.To perform a real-time capture, a client may instantiate AVCaptureSession and add appropriate.

4. Capture Metadata Output
AVCaptureMetadataOutput is a concrete subclass of AVCaptureOutput that can be used to process metadata objects from an attached connection. Instances of AVCaptureMetadataOutput emit arrays of AVMetadataObject instances such as detected faces, barcodes.

5.SetDelegate
Sets the receiver's delegate that will accept metadata objects and dispatch queue on which the delegate will be called. A serial dispatch queue must be used to guarantee that metadata objects will be delivered in order.

6. Set metadataObjectTypes:
Specifies the types of metadata objects that the receiver.

7.Preview:
AVCaptureVideoPreviewLayer quickly render the raw data collected by the camera on the screen, means we can also say, AVCaptureVideoPreviewLayer  is display a preview of what the camera is capturing.
AVCaptureVideoPreviewLayer is a subclass of CALayer that we add as a sublayer of preview view's layer.

8.Start the session
 startRunning to start the flow of data from inputs device to outputs data.

Now add delegate method for receive output data.
-(void) captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
   
    if (metadataObjects != nil && [metadataObjects count]>0) {
        id metadataObj= [metadataObjects objectAtIndex:0];
        if ([metadataObj isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
            

            AVMetadataMachineReadableCodeObject *barCodeObject;
            barCodeObject = (AVMetadataMachineReadableCodeObject *)[self.videoPreviewLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metadataObj];
          
           
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"metadata readable object count = %d   \n Meta Data = %@",(int)metadataObjects.count,metadataObjects);
                self.statusLabel.hidden = NO;
                [self.statusLabel performSelectorOnMainThread:@selector(setText:) withObject:[metadataObj stringValue] waitUntilDone:NO];
                [self stopReading];
                [self.scanButton setTitle:@"Scan" forState:UIControlStateNormal];
                self.scanButton.enabled = YES;
            });
        }
      
    }
}


add another method for stop reading

-(void) stopReading{
    [self.captureSession stopRunning];
    self.captureSession = nil;
}


Now we have enough code for reading a QRcode, we just need to digger our code.

Add one more method for start and stop reading with user interaction.

Declare a  IBAction method in ViewController.h file
-(IBAction)buttonAction:(id)sender
Go to ViewController.h file and define IBAction method

-(IBAction)buttonAction:(id)sender{
   
    if ([self.captureSession isRunning] == YES) {
        self.statusLabel.hidden = YES;
        self.statusLabel.text = @"";
        [self stopReading];
        [self.scanButton setTitle:@"Scan" forState:UIControlStateNormal];
    }
    else{
        if ([self startReading]) {
            self.statusLabel.hidden = NO;
            self.statusLabel.text = @"Scanning...";
            [self startReading];
            [self.scanButton setTitle:@"Stop" forState:UIControlStateNormal];
        }
       
    }
} 


Now go to main storyboard file and connect IBAction method to button click  event.

Now run the App and scan a QR code, you can see the encoded data in QR code on label.

Click here for complete project.

Saturday, 6 August 2016

Create Custom Camera



iOS provide UIIMagePickerController for getting an image from photo library and for capturing a new image. But iOS also provide a cool feature for creating custom camera. We can create custom camera using AVFoundation framework, which give direct access to the camera and microphone.
This process is managed through AVCaptureSession which coordinate the flow of data from input devices (such as camera, microphone) to outputs (Such as image and movie file).

Minimally we need:

1) An instance of AVCaptureDevice to present the input device, such as a camera and microphone.
2) An instance of a concrete subclass of AVCaptureInput to configure the ports from the input device.
3) An instance of a concrete subclass of AVCaptureOutput to manage the output to a movie file or  still image.
4) An instance of AVCaptureSession to coordinate the data flow from input to output.
5) An instance of AVCaptureVideoPreviewLayer to display a preview of what the camera is recording.



Start session:

    self.session = [[AVCaptureSession alloc] init]; //initialize an instance of AVCaptureSession
    [self.session setSessionPreset:AVCaptureSessionPresetHigh];
//Indicates the session preset currently in use by the receiver.

Capture Device: 

An AVCaptureDevice represents a physical device that provides realtime input media data, such as video and audio. A input device can be the camera or microphone.
       
AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

 Capture Device Input:

AVCaptureDeviceInput is a concrete subclass of AVCaptureInput that provides an interface for capturing media from an AVCaptureDevice.

       self.captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];

Now add capture device input to session
      if ( [self.session canAddInput:self.captureDeviceInput] )
            [self.session addInput:self.captureDeviceInput];


Preview:

AVCaptureVideoPreviewLayer quickly render the raw data collected by the camera on the screen, means we can also say, AVCaptureVideoPreviewLayer  is display a preview of what the camera is capturing.
AVCaptureVideoPreviewLayer is a subclass of CALayer that we add as a sublayer of preview view's layer.

AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
   
    CALayer *rootLayer = [[self view] layer];
    [rootLayer setMasksToBounds:YES];
    [previewLayer setFrame:CGRectMake(0, 0, rootLayer.bounds.size.height, rootLayer.bounds.size.height)];
    [self.PreviewView.layer insertSublayer:previewLayer atIndex:0];


Now, Its enough for display camera recording on screen.

Start the session

[self.session startRunning];

Run the app and check.


Now add a UIButton to the view for capture rendered raw data as a image.

and, for manage the output to a still image create an instance of AVCaptureStillImageOutput and add to session.
AVCaptureStillImageOutput is a concrete subclass of AVCaptureOutput that can be used to capture high-quality still images with accompanying metadata.

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    if ([self.session canAddOutput:stillImageOutput])
    {
        [stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}];
        [self.session addOutput:stillImageOutput];
        [self setStillImageOutput:stillImageOutput];
    }


(Add above code before start the session)

Now add button method for capture image,

-(IBAction)captureButtonAction:(id)sender{
    //capture preview image
    [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
       
        if (imageDataSampleBuffer)
        {
            //get image data from buffer
            NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [[UIImage alloc] initWithData:imageData];
        

//save image to photo library (if you want to save captured image)
            [[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];
        }
    }];
}


Now its time to capture fresh image.
Run the app and capture image.


Now Add another method for swap camera(front to back and back to front)
#pragma mark -
-(IBAction) changeCameraAction:(id)sender{
    //disable button
    self.captureButton.enabled = NO;
    self.swapButton.enabled = NO;
    //get current input device
    AVCaptureDevice *currentDevice = self.captureDeviceInput.device;
    AVCaptureDevicePosition preferredPosion = AVCaptureDevicePositionUnspecified;
    AVCaptureDevicePosition currentPosition = currentDevice.position
    ;
    //get new preferred position
    switch (currentPosition) {
        case AVCaptureDevicePositionUnspecified:
            break;
        case AVCaptureDevicePositionBack:
            preferredPosion = AVCaptureDevicePositionFront;
            break;
        case AVCaptureDevicePositionFront:
            preferredPosion = AVCaptureDevicePositionBack;
            break;
    }//end switch case
   
//configure new input device
    AVCaptureDevice *captureDevice = [self deviceWithMediaType:AVMediaTypeVideo withPreferredPosition:preferredPosion];
    AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:nil];
    //update session
    [self.session beginConfiguration];
    // Remove the existing device input first, since using the front and back camera simultaneously is not supported.
    [self.session removeInput:self.captureDeviceInput];
   
    if ( [self.session canAddInput:deviceInput] ) {
       
        [self.session addInput:deviceInput];
        self.captureDeviceInput = deviceInput;
    }
    else {
        [self.session addInput:self.captureDeviceInput];
    }
    [self.session commitConfiguration];
    //enable button
    self.captureButton.enabled = YES;
    self.swapButton.enabled = YES;
}
-(AVCaptureDevice *) deviceWithMediaType:(NSString *)mediaType withPreferredPosition:(AVCaptureDevicePosition) position{
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
    AVCaptureDevice *captureDevice = devices.firstObject;
   
    for ( AVCaptureDevice *device in devices ) {
        if ( device.position == position ) {
            captureDevice = device;
            break;
        }
    }
    return captureDevice;
}


Click here for complete project.