Created
February 1, 2016 10:52
-
-
Save trinnguyen/637eb49a2fcdf1db0099 to your computer and use it in GitHub Desktop.
CameraPreviewView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using AVFoundation; | |
using Foundation; | |
using Masonry; | |
using System.Threading.Tasks; | |
using UIKit; | |
namespace UICode.Camera | |
{ | |
internal class CameraPreviewView : UIView | |
{ | |
private AVCaptureDevice backCamera; | |
private AVCaptureDevice frontCamera; | |
private AVCaptureSession session; | |
private AVCaptureStillImageOutput stillCameraOutput; | |
private AVCaptureVideoPreviewLayer previewLayer; | |
private AVCaptureDeviceInput currentInput; | |
private bool isBack; | |
private UIView overLayView; | |
private UIColor _borderColor; | |
public UIColor BorderColor | |
{ | |
get { return _borderColor; } | |
set | |
{ | |
if (_borderColor != value) | |
{ | |
_borderColor = value; | |
if (overLayView != null) | |
{ | |
overLayView.Layer.MasksToBounds = true; | |
overLayView.Layer.BorderColor = _borderColor.CGColor; | |
overLayView.Layer.BorderWidth = 1f; | |
overLayView.Layer.CornerRadius = 3f; | |
} | |
} | |
} | |
} | |
private int padding; | |
public int Padding | |
{ | |
get { return padding; } | |
set | |
{ | |
if (padding != value) | |
{ | |
padding = value; | |
SetNeedsUpdateConstraints(); | |
} | |
} | |
} | |
public CameraPreviewView() | |
{ | |
//Create overlay | |
overLayView = new UIView(); | |
AddSubview(overLayView); | |
//init Camera | |
InitializeAndSetUp(); | |
//bring overlay to front | |
BringSubviewToFront(overLayView); | |
} | |
private async void InitializeAndSetUp() | |
{ | |
bool accessable = await RequestAuthorization(); | |
if (accessable) | |
{ | |
SetUp(); | |
} | |
else | |
{ | |
ShowAlert("not allowed to access the camera devices"); | |
} | |
} | |
private Task<bool> RequestAuthorization() | |
{ | |
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); | |
var authorizationStatus = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video); | |
switch (authorizationStatus) | |
{ | |
case AVAuthorizationStatus.NotDetermined: | |
AVCaptureDevice.RequestAccessForMediaType(AVMediaType.Video, (granted) => | |
{ | |
tcs.SetResult(granted); | |
}); | |
break; | |
case AVAuthorizationStatus.Authorized: | |
tcs.SetResult(true); | |
break; | |
case AVAuthorizationStatus.Denied: | |
case AVAuthorizationStatus.Restricted: | |
tcs.SetResult(false); | |
break; | |
default: | |
tcs.SetResult(false); | |
break; | |
} | |
return tcs.Task; | |
} | |
private void SetUp() | |
{ | |
//session | |
session = new AVCaptureSession(); | |
var availableCameraDevices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video); | |
if (availableCameraDevices == null || availableCameraDevices.Length == 0) | |
{ | |
ShowAlert("No Camera"); | |
return; | |
} | |
foreach (var device in availableCameraDevices) | |
{ | |
if (device.Position == AVCaptureDevicePosition.Back) | |
{ | |
backCamera = device; | |
} | |
else if (device.Position == AVCaptureDevicePosition.Front) | |
{ | |
frontCamera = device; | |
} | |
} | |
//input | |
NSError error; | |
var backCameraInput = AVCaptureDeviceInput.FromDevice(backCamera, out error); | |
if (backCameraInput != null) | |
{ | |
if (session.CanAddInput(backCameraInput)) | |
session.AddInput(backCameraInput); | |
currentInput = backCameraInput; | |
} | |
isBack = true; | |
//Preview | |
previewLayer = AVCaptureVideoPreviewLayer.FromSession(session); | |
previewLayer.Frame = Bounds; | |
previewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill; | |
Layer.AddSublayer(previewLayer); | |
//Configure | |
session.SessionPreset = AVCaptureSession.PresetPhoto; | |
//output | |
stillCameraOutput = new AVCaptureStillImageOutput(); | |
if (session.CanAddOutput(stillCameraOutput)) | |
{ | |
session.AddOutput(stillCameraOutput); | |
} | |
//Start | |
Start(); | |
} | |
private void ShowAlert(string message) | |
{ | |
UIAlertView alertView = new UIAlertView("Error", message, null, "OK", null); | |
alertView.Show(); | |
} | |
public override void LayoutIfNeeded() | |
{ | |
base.LayoutIfNeeded(); | |
if (previewLayer != null) | |
previewLayer.Frame = Bounds; | |
} | |
public override void UpdateConstraints() | |
{ | |
overLayView.RemakeConstraints((maker) => | |
{ | |
maker.Edges.EqualTo(this).Insets(new UIEdgeInsets(Padding, Padding, Padding, Padding)); | |
}); | |
base.UpdateConstraints(); | |
if (previewLayer != null) | |
previewLayer.Frame = Bounds; | |
} | |
public void ForceLayout() | |
{ | |
System.Diagnostics.Debug.WriteLine("Force Layout: " + Bounds); | |
if (previewLayer != null) | |
{ | |
previewLayer.Frame = Bounds; | |
if (previewLayer.Connection.SupportsVideoOrientation) | |
{ | |
previewLayer.Connection.VideoOrientation = RefreshDeviceOrientation(); | |
} | |
} | |
} | |
private AVCaptureVideoOrientation RefreshDeviceOrientation() | |
{ | |
var orientation = UIApplication.SharedApplication.StatusBarOrientation; | |
switch (orientation) | |
{ | |
case UIInterfaceOrientation.Portrait: | |
return AVCaptureVideoOrientation.Portrait; | |
case UIInterfaceOrientation.PortraitUpsideDown: | |
return AVCaptureVideoOrientation.PortraitUpsideDown; | |
case UIInterfaceOrientation.LandscapeLeft: | |
return AVCaptureVideoOrientation.LandscapeLeft; | |
case UIInterfaceOrientation.LandscapeRight: | |
return AVCaptureVideoOrientation.LandscapeRight; | |
default: | |
break; | |
} | |
return AVCaptureVideoOrientation.Portrait; | |
} | |
public void Start() | |
{ | |
if (session != null) | |
session.StartRunning(); | |
} | |
public void Stop() | |
{ | |
if (session != null) | |
session.StopRunning(); | |
} | |
public void ToggleCamera() | |
{ | |
if (previewLayer == null) | |
return; | |
session.BeginConfiguration(); | |
NSError error = null; | |
session.RemoveInput(currentInput); | |
if (isBack) | |
{ | |
var frontCameraInput = AVCaptureDeviceInput.FromDevice(frontCamera, out error); | |
if (frontCameraInput != null) | |
{ | |
if (session.CanAddInput(frontCameraInput)) | |
session.AddInput(frontCameraInput); | |
currentInput = frontCameraInput; | |
} | |
} | |
else | |
{ | |
var backCameraInput = AVCaptureDeviceInput.FromDevice(backCamera, out error); | |
if (backCameraInput != null) | |
{ | |
if (session.CanAddInput(backCameraInput)) | |
session.AddInput(backCameraInput); | |
currentInput = backCameraInput; | |
} | |
} | |
isBack = !isBack; | |
session.CommitConfiguration(); | |
} | |
public Task<UIImage> Capture() | |
{ | |
if (previewLayer == null) | |
return new Task<UIImage>(() => null); | |
TaskCompletionSource<UIImage> tcs = new TaskCompletionSource<UIImage>(); | |
var connection = stillCameraOutput.ConnectionFromMediaType(AVMediaType.Video); | |
connection.VideoOrientation = (AVCaptureVideoOrientation)((long)(UIDevice.CurrentDevice.Orientation)); | |
stillCameraOutput.CaptureStillImageAsynchronously(connection, delegate (CoreMedia.CMSampleBuffer imageDataSampleBuffer, NSError error) | |
{ | |
if (error == null) | |
{ | |
var imageData = AVCaptureStillImageOutput.JpegStillToNSData(imageDataSampleBuffer); | |
var uiimage = new UIImage(imageData); | |
tcs.SetResult(uiimage); | |
} | |
else | |
{ | |
ShowAlert("error while capturing still image: " + error.Description); | |
tcs.SetResult(null); | |
} | |
}); | |
return tcs.Task; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment