AV Foundation is an Apple system framework that exists on macOS and iOS, along with watchOS and tvOS. The goal of this tutorial will be to help you build a fully functional iOS app that’s capable of capturing photos and videos using the device’s cameras. We’ll also be following the principles of good object oriented programming and designing a utility class that can be reused and extended in all your projects.
What is AV Foundation?
"AV Foundation is the full featured framework for working with time-based audiovisual media on iOS, macOS, watchOS and tvOS. Using AV Foundation, you can easily play, create, and edit QuickTime movies and MPEG-4 files, play HLS streams, and build powerful media functionality into your apps." – Apple
So, there you have it. AV Foundation is a framework for capturing, processing, and editing audio and video on Apple devices.
Your app should use the prebuilt camera and media control if,
- You prioritize code sharing over customization.
- Camera or media functionality is just a small part of your overall application.
Your app should use a custom camera control if,
- You prioritize customization over code sharing.
- You don’t want to navigate the user to a new view to take a photo.
- The core experience of the app revolves around the camera.
AVFoundation
is a namespace that contains classes for high-level recording and playback capabilities for audio and video on iOS.
How do you develop in Xamarin Forms ?
Step 1
You have to create CameraPreview which inherits from View and Bindable Property,
- public class CameraPreview: View {
- public static readonly BindableProperty CameraProperty = BindableProperty.Create(propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraPreview), defaultValue: CameraOptions.Rear);
- public CameraOptions Camera {
- get {
- return (CameraOptions) GetValue(CameraProperty);
- }
- set {
- SetValue(CameraProperty, value);
- }
- }
- }
CameraOptions contains Enum,
- public enum CameraOptions {
- Rear,
- Front
- }
Step 3
Now we're using our own CamaraPreview in Xaml like below
- <Grid
- BackgroundColor="Transparent"
- HorizontalOptions="FillAndExpand"
- RowSpacing="0"
- VerticalOptions="FillAndExpand"
- >
- <Grid.RowDefinitions>
- <RowDefinition Height="*" />
- <RowDefinition Height="250" />
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*" />
- </Grid.ColumnDefinitions>
- <local:CameraPreview
- Grid.Row="0"
- Grid.Column="0"
- Camera="Rear"
- HeightRequest="300"
- HorizontalOptions="FillAndExpand"
- VerticalOptions="FillAndExpand"
- WidthRequest="400"
- />
- <frame
- Grid.Row="0"
- Grid.Column="0"
- BackgroundColor="Transparent"
- BorderColor="White"
- HeightRequest="100"
- HorizontalOptions="Center"
- VerticalOptions="Center"
- WidthRequest="200"
- />
-
- <StackLayout
- Grid.Row="1"
- Grid.Column="0"
- BackgroundColor="Black"
- Orientation="Vertical"
- >
- <label
- FontSize="Title"
- HorizontalOptions="CenterAndExpand"
- Text="Add Card"
- TextColor="White"
- />
- <label
- FontSize="Large"
- HorizontalOptions="CenterAndExpand"
- Text="Position your card in the frame"
- TextColor="White"
- />
- </StackLayout>
- </Grid>
Now the most important part to is to create renderer to write code in the platform part,
- IOS
- CameraPreviewRenderer & UICameraPreview
CameraPreviewRenderer.cs
- using System;
- using AVFoundation;
- using CustomRenderer;
- using CustomRenderer.iOS;
- using Foundation;
- using Xamarin.Forms;
- using Xamarin.Forms.Platform.iOS;
- [assembly: ExportRenderer(typeof(CameraPreview), typeof(CameraPreviewRenderer))]
- namespace CodeXamarin.iOS {
- public class CameraPreviewRenderer: ViewRenderer < CameraPreview, UICameraPreview > {
- UICameraPreview uiCameraPreview;
- private AVCaptureOutput cameraOutput;
- private AVCaptureStillImageOutput stillImageOutput;
- protected override void OnElementChanged(ElementChangedEventArgs < CameraPreview > e) {
- base.OnElementChanged(e);
- if (e.OldElement != null) {
-
- uiCameraPreview.Tapped -= OnCameraPreviewTapped;
- }
- if (e.NewElement != null) {
- if (Control == null) {
- uiCameraPreview = new UICameraPreview(e.NewElement.Camera);
- SetNativeControl(uiCameraPreview);
- }
-
- uiCameraPreview.Tapped += OnCameraPreviewTapped;
- }
- }
- async void OnCameraPreviewTapped(object sender, EventArgs e) {
- if (uiCameraPreview.IsPreviewing) {
- var videoConnection = uiCameraPreview.stillImageOutput.ConnectionFromMediaType(AVMediaType.Video);
- var sampleBuffer = await uiCameraPreview.stillImageOutput.CaptureStillImageTaskAsync(videoConnection);
- var jpegImageAsNsData = AVCaptureStillImageOutput.JpegStillToNSData(sampleBuffer);
- var jpegAsByteArray = jpegImageAsNsData.ToArray();
- uiCameraPreview.CaptureSession.StopRunning();
- uiCameraPreview.IsPreviewing = false;
- } else {
- uiCameraPreview.CaptureSession.StartRunning();
- uiCameraPreview.IsPreviewing = true;
- }
- }
- protected override void Dispose(bool disposing) {
- if (disposing) {
- Control.CaptureSession.Dispose();
- Control.Dispose();
- }
- base.Dispose(disposing);
- }
- }
- }
UICameraPreview.cs
- using System;
- using System.Linq;
- using AVFoundation;
- using CoreGraphics;
- using Foundation;
- using UIKit;
- namespace CustomRenderer.iOS {
- public class UICameraPreview: UIView {
- AVCaptureVideoPreviewLayer previewLayer;
- CameraOptions cameraOptions;
- public AVCaptureStillImageOutput stillImageOutput;
- public event EventHandler < EventArgs > Tapped;
- public AVCaptureSession CaptureSession {
- get;
- private set;
- }
- public bool IsPreviewing {
- get;
- set;
- }
- public UICameraPreview(CameraOptions options) {
- cameraOptions = options;
- IsPreviewing = false;
- Initialize();
- }
- public override void LayoutSubviews() {
- base.LayoutSubviews();
- if (previewLayer != null) previewLayer.Frame = Bounds;
- }
- public override void TouchesBegan(NSSet touches, UIEvent evt) {
- base.TouchesBegan(touches, evt);
- OnTapped();
- }
- protected virtual void OnTapped() {
- var eventHandler = Tapped;
- if (eventHandler != null) {
- eventHandler(this, new EventArgs());
- }
- }
- void Initialize() {
- CaptureSession = new AVCaptureSession();
- previewLayer = new AVCaptureVideoPreviewLayer(CaptureSession) {
- Frame = Bounds,
- VideoGravity = AVLayerVideoGravity.ResizeAspectFill
- };
- var videoDevices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
- var cameraPosition = (cameraOptions == CameraOptions.Front) ? AVCaptureDevicePosition.Front : AVCaptureDevicePosition.Back;
- var device = videoDevices.FirstOrDefault(d => d.Position == cameraPosition);
- if (device == null) {
- return;
- }
- var dictionary = new NSMutableDictionary();
- dictionary[AVVideo.CodecKey] = new NSNumber((int) AVVideoCodec.JPEG);
- stillImageOutput = new AVCaptureStillImageOutput() {
- OutputSettings = new NSDictionary()
- };
- CaptureSession.AddOutput(stillImageOutput);
- NSError error;
- var input = new AVCaptureDeviceInput(device, out error);
- CaptureSession.AddInput(input);
- Layer.AddSublayer(previewLayer);
- CaptureSession.StartRunning();
- IsPreviewing = true;
- }
- }
- }
I am not writing here for Android Platform Code. You can download the code and check it out. The CameraPreviewRenderer Android file is there.
Please comment if you found this useful for you, or ask any query and I will try to provide you with more details on your query.
Reference
- https://guides.codepath.com/ios/Creating-a-Custom-Camera-View
- https://devblogs.microsoft.com/xamarin/custom-camera-display-avfoundation/