Former-commit-id: c84c98c0255c7648475fb659f49d901425434f9c
TangShanKaiPing
wanggang 6 years ago
parent 62b01ed6aa
commit 47dff34b9e

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using Android.Content;
using Android.Hardware;
using Android.Runtime;
using Android.Views;
namespace Demo.Droid.Controls
{
public sealed class CameraPreview : ViewGroup, ISurfaceHolderCallback
{
private SurfaceView surfaceView;
private ISurfaceHolder holder;
private Camera.Size previewSize;
private IList<Camera.Size> supportedPreviewSizes;
private Camera camera;
private IWindowManager windowManager;
public bool IsPreviewing { get; set; }
public Camera Preview
{
get { return camera; }
set
{
camera = value;
if (camera != null)
{
supportedPreviewSizes = Preview.GetParameters().SupportedPreviewSizes;
RequestLayout();
}
}
}
public CameraPreview(Context context)
: base(context)
{
surfaceView = new SurfaceView(context);
AddView(surfaceView);
windowManager = Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
IsPreviewing = false;
holder = surfaceView.Holder;
holder.AddCallback(this);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec);
int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec);
SetMeasuredDimension(width, height);
if (supportedPreviewSizes != null)
{
previewSize = GetOptimalPreviewSize(supportedPreviewSizes, width, height);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
surfaceView.Measure(msw, msh);
surfaceView.Layout(0, 0, r - l, b - t);
}
public void SurfaceCreated(ISurfaceHolder holder)
{
try
{
if (Preview != null)
{
Preview.SetPreviewDisplay(holder);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(@" ERROR: ", ex.Message);
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
if (Preview != null)
{
Preview.StopPreview();
}
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
var parameters = Preview.GetParameters();
parameters.SetPreviewSize(previewSize.Width, previewSize.Height);
RequestLayout();
switch (windowManager.DefaultDisplay.Rotation)
{
case SurfaceOrientation.Rotation0:
camera.SetDisplayOrientation(90);
break;
case SurfaceOrientation.Rotation90:
camera.SetDisplayOrientation(0);
break;
case SurfaceOrientation.Rotation270:
camera.SetDisplayOrientation(180);
break;
}
Preview.SetParameters(parameters);
Preview.StartPreview();
IsPreviewing = true;
}
private Camera.Size GetOptimalPreviewSize(IList<Camera.Size> sizes, int w, int h)
{
const double AspectTolerance = 0.1;
double targetRatio = (double)w / h;
if (sizes == null)
{
return null;
}
Camera.Size optimalSize = null;
double minDiff = double.MaxValue;
int targetHeight = h;
foreach (Camera.Size size in sizes)
{
double ratio = (double)size.Width / size.Height;
if (Math.Abs(ratio - targetRatio) > AspectTolerance)
continue;
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
if (optimalSize == null)
{
minDiff = double.MaxValue;
foreach (Camera.Size size in sizes)
{
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
}
return optimalSize;
}
}
}

@ -0,0 +1,48 @@
using Android.Content;
using Android.Hardware;
using Demo.Droid.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Demo.Controls.CameraPreview), typeof(CameraPreviewRenderer))]
namespace Demo.Droid.Controls
{
public class CameraPreviewRenderer : ViewRenderer<Demo.Controls.CameraPreview, Demo.Droid.Controls.CameraPreview>
{
private CameraPreview cameraPreview;
public CameraPreviewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Demo.Controls.CameraPreview> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control == null)
{
cameraPreview = new CameraPreview(Context);
SetNativeControl(cameraPreview);
}
var camera = Camera.Open((int)e.NewElement.Camera);
var parameters = camera.GetParameters();
var width = parameters.PreviewSize.Width;
var height = parameters.PreviewSize.Height;
var format = parameters.PreviewFormat;
camera.SetPreviewCallback(new PreviewCallback(width, height, format));
Control.Preview = camera;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.Preview.Release();
}
base.Dispose(disposing);
}
}
}

@ -0,0 +1,47 @@
using Android.Hardware;
using SkiaSharp.Views.Android;
using System;
using System.IO;
namespace Demo.Droid.Controls
{
public class PreviewCallback : Java.Lang.Object, Camera.IPreviewCallback
{
private int width;
private int height;
private Android.Graphics.ImageFormatType previewFormat;
public PreviewCallback(int width, int height, Android.Graphics.ImageFormatType previewFormat)
{
this.width = width;
this.height = height;
this.previewFormat = previewFormat;
}
public void OnPreviewFrame(byte[] data, Camera camera)
{
//byte[] rotatedData = new byte[bytes.Length];
//for (int y = 0; y < height; y++)
//{
// for (int x = 0; x < width; x++)
// {
// rotatedData[x * height + height - y - 1] = bytes[x + y * width];
// }
//}
var jpegBytes = this.ConvertYuvToJpeg(data);
var droidBitmap = Android.Graphics.BitmapFactory.DecodeByteArray(jpegBytes, 0, jpegBytes.Length);
var skBitmap = droidBitmap.ToSKBitmap();
var base64 = Convert.ToBase64String(jpegBytes);
}
private byte[] ConvertYuvToJpeg(byte[] yuvData, int quality = 80)
{
var yuv = new Android.Graphics.YuvImage(yuvData, previewFormat, width, height, null);
var ms = new MemoryStream();
yuv.CompressToJpeg(new Android.Graphics.Rect(0, 0, width, height), quality, ms);
var jpegData = ms.ToArray();
ms.Close();
return jpegData;
}
}
}

@ -88,6 +88,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AndroidDecoder.cs" />
<Compile Include="Controls\CameraPreview.cs" />
<Compile Include="Controls\CameraPreviewRenderer.cs" />
<Compile Include="Controls\PreviewCallback.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

@ -0,0 +1,36 @@
using Demo.iOS.Controls;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(Demo.Controls.CameraPreview), typeof(CameraPreviewRenderer))]
namespace Demo.iOS.Controls
{
public class CameraPreviewRenderer : ViewRenderer<Demo.Controls.CameraPreview, UICameraPreview>
{
private UICameraPreview uiCameraPreview;
protected override void OnElementChanged(ElementChangedEventArgs<Demo.Controls.CameraPreview> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control == null)
{
uiCameraPreview = new UICameraPreview(e.NewElement.Camera);
SetNativeControl(uiCameraPreview);
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.CaptureSession.Dispose();
Control.Dispose();
}
base.Dispose(disposing);
}
}
}

@ -0,0 +1,65 @@
using AVFoundation;
using CoreGraphics;
using CoreMedia;
using CoreVideo;
using Foundation;
using SkiaSharp.Views.iOS;
using System;
using System.IO;
using UIKit;
namespace Demo.iOS.Controls
{
public class SampleBufferDelegate : AVCaptureVideoDataOutputSampleBufferDelegate
{
public override void DidOutputSampleBuffer(AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
try
{
using (var pixelBuffer = sampleBuffer.GetImageBuffer() as CVPixelBuffer)
{
pixelBuffer.Lock(CVPixelBufferLock.None);
var flags = CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Little;
using (var cs = CGColorSpace.CreateDeviceRGB())
{
using (var context = new CGBitmapContext(pixelBuffer.BaseAddress,
pixelBuffer.Width,
pixelBuffer.Height,
8,
pixelBuffer.BytesPerRow,
cs,
(CGImageAlphaInfo)flags))
{
using (var cgImage = context.ToImage())
{
pixelBuffer.Unlock(CVPixelBufferLock.None);
var uiImage = UIImage.FromImage(cgImage);
using (var stream = new MemoryStream())
{
uiImage.AsJPEG().AsStream().CopyTo(stream);
var jpegBytes = stream.ToArray();
var skBitmap = cgImage.ToSKBitmap();
var base64 = Convert.ToBase64String(jpegBytes);
}
}
}
}
}
}
catch (Exception ex)
{
ex.ToString();
}
finally
{
try
{
sampleBuffer.Dispose();
}
catch
{
}
}
}
}
}

@ -0,0 +1,68 @@
using AVFoundation;
using CoreFoundation;
using CoreGraphics;
using CoreVideo;
using Demo.Controls;
using Foundation;
using System;
using System.Linq;
using UIKit;
namespace Demo.iOS.Controls
{
public class UICameraPreview : UIView
{
private AVCaptureVideoPreviewLayer previewLayer;
private CameraOptions cameraOptions;
public AVCaptureSession CaptureSession { get; private set; }
public bool IsPreviewing { get; set; }
public UICameraPreview(CameraOptions options)
{
cameraOptions = options;
IsPreviewing = false;
Initialize();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
previewLayer.Frame = rect;
}
private 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;
}
NSError error;
var input = new AVCaptureDeviceInput(device, out error);
CaptureSession.AddInput(input);
//
var output = new AVCaptureVideoDataOutput();//CVPixelFormatType.CV32BGRA
output.WeakVideoSettings = new CVPixelBufferAttributes { PixelFormatType = CVPixelFormatType.CV32BGRA }.Dictionary;
output.SetSampleBufferDelegateQueue(new SampleBufferDelegate(), new DispatchQueue("myQueue"));
CaptureSession.AddOutput(output);
//
Layer.AddSublayer(previewLayer);
CaptureSession.StartRunning();
IsPreviewing = true;
}
}
}

@ -101,6 +101,9 @@
<AppExtensionDebugBundleId />
</PropertyGroup>
<ItemGroup>
<Compile Include="Controls\SampleBufferDelegate.cs" />
<Compile Include="Controls\CameraPreviewRenderer.cs" />
<Compile Include="Controls\UICameraPreview.cs" />
<Compile Include="IosDecoder.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />

@ -0,0 +1,8 @@
namespace Demo.Controls
{
public enum CameraOptions
{
Rear,
Front
}
}

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace Demo.Controls
{
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); }
}
}
}

@ -15,6 +15,7 @@
<PackageReference Include="Flurl" Version="2.8.2" />
<PackageReference Include="LibVLCSharp.Forms" Version="3.0.2" />
<PackageReference Include="NetCoreSerial" Version="1.3.1" />
<PackageReference Include="SkiaSharp.Views" Version="1.68.0" />
<PackageReference Include="Xam.Plugin.Media" Version="4.0.1.5" />
<PackageReference Include="Xam.Plugins.Notifier" Version="3.0.1" />
<PackageReference Include="Xamarin.Forms" Version="4.0.0.482894" />
@ -25,4 +26,10 @@
<ItemGroup>
<ProjectReference Include="..\..\IoT\IoT.Resources\IoT.Resources.Standard.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="QrScanPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

@ -92,10 +92,10 @@ namespace Demo
e.Cancel = true;
CrossLocalNotifications.Current.Show("title", e.Url);
}
else if (e.Url.Contains("webqr.html"))
else if (e.Url.Contains("qr.html"))
{
e.Cancel = true;
Scan();
OpenQrScanPage();
}
else if (e.Url.Contains("face.html"))
{
@ -118,26 +118,35 @@ namespace Demo
}
}
private void Scan()
//private void Scan()
//{
// var scanPage = new ZXingScannerPage();
// scanPage.OnScanResult += (result) =>
// {
// scanPage.IsScanning = false;
// Device.BeginInvokeOnMainThread(async () =>
// {
// await Navigation.PopAsync();
// });
// };
// this.Navigation.PushAsync(scanPage);
//}
private async Task OpenQrScanPage()
{
var scanPage = new ZXingScannerPage();
scanPage.OnScanResult += (result) =>
if (await CheckCameraPermisstions())
{
scanPage.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
});
};
this.Navigation.PushAsync(scanPage);
var scanPage = new QrScanPage();
await this.Navigation.PushAsync(scanPage);
}
}
private async Task FaceAsync()
{
if (!await CheckCameraPermisstions())
return;
var facePage = new FacePage(new ScanningOptionsBase { UseFrontCameraIfAvailable = true }, new ZXingOverlay());
await Navigation.PushAsync(facePage);
if (await CheckCameraPermisstions())
{
var facePage = new FacePage(new ScanningOptionsBase { UseFrontCameraIfAvailable = true }, new ZXingOverlay());
await Navigation.PushAsync(facePage);
}
}
public void Alert(object message, string title = "Ìáʾ", string button = "È·¶¨")

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Demo.Controls"
x:Class="Demo.QrScanPage">
<ContentPage.Content>
<StackLayout>
<local:CameraPreview Camera="Rear" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

@ -0,0 +1,14 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Demo
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class QrScanPage : ContentPage
{
public QrScanPage()
{
InitializeComponent();
}
}
}

@ -27,7 +27,7 @@
<f7-navbar>
<f7-nav-title>登录</f7-nav-title>
<f7-nav-right>
<f7-link external panel-open="right" icon-f7="add" href="face.html"></f7-link>
<f7-link external panel-open="right" icon-f7="add" href="qr.html"></f7-link>
</f7-nav-right>
</f7-navbar>
<form v-bind:action="Server+'/Account/AppLogin'" v-on:submit.prevent="OnSubmit" class="login">

Loading…
Cancel
Save