Skip to content

Instantly share code, notes, and snippets.

@Alxandr
Created July 2, 2012 17:47
Show Gist options
  • Save Alxandr/3034545 to your computer and use it in GitHub Desktop.
Save Alxandr/3034545 to your computer and use it in GitHub Desktop.
using System;
using MonoTouch.UIKit;
using com.google.zxing;
using MonoTouch.AVFoundation;
using MonoTouch.CoreFoundation;
using MonoTouch.CoreVideo;
using System.Collections;
using MonoTouch.CoreMedia;
using System.Runtime.InteropServices;
using com.google.zxing.common;
namespace FdvWeb
{
// based on https://github.com/xamarin/monotouch-samples/blob/master/AVCaptureFrames/Main.cs
public class QRScanViewController : UIViewController
{
public event Action<Result> Scan;
AVCaptureSession session;
AVCaptureVideoPreviewLayer previewLayer;
AVCaptureDevice captureDevice;
UIImageView overlayView;
UIButton buttonCancel;
UIButton buttonFlash;
DispatchQueue queue;
ZxingScanner scanner;
public override void LoadView ()
{
base.LoadView ();
if (!SetupCaptureSession ())
throw new NotSupportedException ("Unable to setup camera for scan");
previewLayer.Frame = UIScreen.MainScreen.Bounds;
View.Layer.AddSublayer (previewLayer);
}
public override void ViewDidLoad ()
{
overlayView = new UIImageView (UIScreen.MainScreen.Bounds);
//Use your own overlay image here
//overlayView.Image = UIImage.FromFile("Images/BarCodeOverlay.png");
this.View.AddSubview (overlayView);
buttonCancel = new UIButton ();
//buttonCancel.ButtonType = UIButtonType.RoundedRect;
buttonCancel.Frame = new System.Drawing.RectangleF (20, 20, 130, 30);
buttonCancel.SetTitle ("Cancel", UIControlState.Normal);
buttonCancel.Alpha = 0.3f;
buttonCancel.SetTitleColor (UIColor.White, UIControlState.Normal);
buttonCancel.TintColor = UIColor.Gray;
buttonCancel.TouchUpInside += (sender, e) => {
this.Scan (null);
};
this.View.AddSubview (buttonCancel);
buttonFlash = new UIButton ();
//buttonFlash.ButtonType = UIButtonType.RoundedRect;
buttonFlash.Frame = new System.Drawing.RectangleF (170, 20, 130, 30);
buttonFlash.SetTitle ("Flash On", UIControlState.Normal);
buttonFlash.Alpha = 0.3f;
buttonFlash.TintColor = UIColor.Gray;
buttonFlash.SetTitleColor (UIColor.White, UIControlState.Normal);
//buttonFlash.HorizontalAlignment = UIControlContentHorizontalAlignment.Right;
buttonFlash.TouchUpInside += (sender, e) => {
if (captureDevice != null) {
if (captureDevice.TorchAvailable) {
MonoTouch.Foundation.NSError err = null;
captureDevice.LockForConfiguration (out err);
if (captureDevice.TorchMode == AVCaptureTorchMode.Auto || captureDevice.TorchMode == AVCaptureTorchMode.Off)
captureDevice.TorchMode = AVCaptureTorchMode.On;
else
captureDevice.TorchMode = AVCaptureTorchMode.Off;
captureDevice.UnlockForConfiguration ();
this.BeginInvokeOnMainThread (() => {
if (buttonFlash.CurrentTitle == "Flash On")
buttonFlash.SetTitle ("Flash Off", UIControlState.Normal);
else
buttonFlash.SetTitle ("Flash On", UIControlState.Normal);
}
);
}
}
};
this.View.AddSubview (buttonFlash);
}
bool SetupCaptureSession ()
{
// configure the capture session for low resolution, change this if your code
// can cope with more data or volume
session = new AVCaptureSession () {
SessionPreset = AVCaptureSession.PresetMedium
};
// create a device input and attach it to the session
captureDevice = AVCaptureDevice.DefaultDeviceWithMediaType (AVMediaType.Video);
var input = AVCaptureDeviceInput.FromDevice (captureDevice);
if (input == null) {
// No input device
return false;
}
session.AddInput (input);
// create a VideoDataOutput and add it to the sesion
var output = new AVCaptureVideoDataOutput () {
VideoSettings = new AVVideoSettings (CVPixelFormatType.CV32BGRA)
};
// configure the output
queue = new DispatchQueue ("myQueue");
scanner = new ZxingScanner (this);
output.SetSampleBufferDelegateAndQueue (scanner, queue);
session.AddOutput (output);
previewLayer = new AVCaptureVideoPreviewLayer (session);
previewLayer.Orientation = AVCaptureVideoOrientation.Portrait;
previewLayer.VideoGravity = "AVLayerVideoGravityResizeAspectFill";
session.StartRunning ();
return true;
}
class ZxingScanner : AVCaptureVideoDataOutputSampleBufferDelegate
{
bool gotResult = false;
QRScanViewController parent;
MultiFormatReader reader;
byte[] bytes;
public ZxingScanner (QRScanViewController parent)
{
this.parent = parent;
this.reader = new MultiFormatReader {
Hints = new Hashtable {
{ DecodeHintType.POSSIBLE_FORMATS, new ArrayList {
/*BarcodeFormat.UPC_A, BarcodeFormat.UPC_E , BarcodeFormat.CODE_128,
BarcodeFormat.CODE_39, BarcodeFormat.EAN_13, BarcodeFormat.EAN_8,*/ BarcodeFormat.QR_CODE
} }
}
};
}
public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
try {
if (!gotResult) {
LuminanceSource luminance;
connection.VideoOrientation = AVCaptureVideoOrientation.Portrait;
using (var pixelBuffer = sampleBuffer.GetImageBuffer () as CVPixelBuffer) {
if (bytes == null)
bytes = new byte [pixelBuffer.Height * pixelBuffer.BytesPerRow];
pixelBuffer.Lock (0);
Marshal.Copy (pixelBuffer.BaseAddress, bytes, 0, bytes.Length);
luminance = new RGBLuminanceSource (bytes, pixelBuffer.Width, pixelBuffer.Height);
pixelBuffer.Unlock (0);
}
var binarized = new BinaryBitmap (new HybridBinarizer (luminance));
var result = reader.decodeWithState (binarized);
//parent.session.StopRunning ();
gotResult = true;
if (parent.Scan != null) {
//MonoTouch.AudioToolbox.SystemSound.FromFile ("Sounds/beep.wav").PlayAlertSound ();
parent.Scan (result);
}
}
} catch (ReaderException) {
// ignore this exception; it happens every time there is a failed scan
} catch (Exception e) {
// TODO: this one is unexpected.. log or otherwise handle it
throw;
} finally {
try {
// lamest thing, but seems that this throws :(
sampleBuffer.Dispose ();
} catch {
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment