Я пытаюсь сделать собственный рендерер в своем приложении Xamarin.Form для отображения Camera Stream, iOS работает просто отлично, но Android заставляет меня плакать, На данный момент я не знаю, что делать.
Предварительный просмотр камеры работает очень хорошо, и я даже могу переключаться между передней камерой и задней камерой, но я не могу сделать снимок, вся камера передается в SurfaceView в соответствии с этим уроком: https://docs.microsoft.com/it-it/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view
вот мой код:
using System;
using Android.Graphics;
using Android.Content;
using Android.Runtime;
using Android.Views;
using System.Collections.Generic;
using System.Threading.Tasks;
using Hangover.Camera.Factory;
using Android.Content.PM;
using System.IO;
using Hangover.Autorizzazioni;
using static Android.Hardware.Camera;
using Android.Hardware;
namespace Hangover.Droid.CustomRenderer
{
public class CameraPreview : ViewGroup, ISurfaceHolderCallback,IPictureCallback
{
private SurfaceView surfaceView; // view dove verrà visualizzato lo stream
private ISurfaceHolder holder; // notifica i cambiamenti dell'interfaccia
private Android.Hardware.Camera.Size previewSize;
private IList<Android.Hardware.Camera.Size> supportedPreviewSizes;
public Android.Hardware.Camera HardwareCamera;
private byte[] pictureTaken = null; // foto scattata
IWindowManager windowManager;
public Android.Hardware.Camera PreviewCamera
{
get { return HardwareCamera; }
set
{
HardwareCamera = value;
if (HardwareCamera != null)
{
this.supportedPreviewSizes = PreviewCamera.GetParameters().SupportedPreviewSizes;
RequestLayout();
}
}
}
public CameraPreview(Context context)
: base(context)
{
if (HardwareCamera == null)
this.retryCameraAccess();
surfaceView = new SurfaceView(context);
AddView(surfaceView);
windowManager = Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
holder = surfaceView.Holder;
holder.AddCallback(this);
}
//Inizializza di nuovo la fotocamera in caso non sia stato possibile accedervi
private void retryCameraAccess()
{
try
{
PreviewCamera = Android.Hardware.Camera.Open((int)Android.Hardware.Camera.CameraInfo.CameraFacingBack);
}
catch (Exception e)
{
Console.Write(e);
return;
}
}
#region funzioni view
//Gestisce la rotazione della fotocamera
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
try
{
// Now that the size is known, set up the camera parameters and begin
// the preview.
var parameters = PreviewCamera.GetParameters();
parameters.SetPreviewSize(previewSize.Width, previewSize.Height);
RequestLayout();
PreviewCamera.SetParameters(parameters);
Console.WriteLine("Surface changed");
PreviewCamera.SetPreviewDisplay(holder);
// Important: Call startPreview() to start updating the preview surface.
// Preview must be started before you can take a picture.
PreviewCamera.StartPreview();
this.avviaViewAnteprimaFotocamera();
}
catch (Exception e)
{ Console.Write(e);}
}
// view fotocamera allocata
public void SurfaceCreated(ISurfaceHolder holder)
{
try
{
}
catch (Java.IO.IOException exception)
{Console.Write(exception);}
}
// view fotocamera deallocata
public void SurfaceDestroyed(ISurfaceHolder holder)
{
try
{
}
catch (Java.IO.IOException exception)
{ Console.Write(exception); }
}
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);
}
//Viene chiamato per determinare la grandezza della view e dei suoi figli
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec);
int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec);
SetMeasuredDimension(width, height);
//androidCamera.GetOptimalPreviewSize(width, height);
if (supportedPreviewSizes != null)
previewSize = GetOptimalPreviewSize(supportedPreviewSizes, width, height);
}
#endregion
#region metodi supporto
/*
* Avvia la view che contiene l'anteprima della fotocamera
*/
private void avviaViewAnteprimaFotocamera(){
var parameters = PreviewCamera.GetParameters();
parameters.SetPreviewSize(previewSize.Width, previewSize.Height);
RequestLayout();
switch (windowManager.DefaultDisplay.Rotation)
{
case SurfaceOrientation.Rotation0:
HardwareCamera.SetDisplayOrientation(90);
break;
case SurfaceOrientation.Rotation90:
HardwareCamera.SetDisplayOrientation(0);
break;
case SurfaceOrientation.Rotation270:
HardwareCamera.SetDisplayOrientation(180);
break;
}
PreviewCamera.SetParameters(parameters);
PreviewCamera.StartPreview();
}
//restituisce la grandezza ottimale della view per visualizzare lo stream della fotocamera
private Android.Hardware.Camera.Size GetOptimalPreviewSize(IList<Android.Hardware.Camera.Size> sizes, int w, int h)
{
const double AspectTolerance = 0.1;
double targetRatio = (double)w / h;
if (sizes == null)
{
return null;
}
Android.Hardware.Camera.Size optimalSize = null;
double minDiff = double.MaxValue;
int targetHeight = h;
foreach (Android.Hardware.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 (Android.Hardware.Camera.Size size in sizes)
{
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
}
return optimalSize;
}
#endregion
#region funzioniHardware
/// <summary>
/// attiva il flash
/// </summary>
public void activate_androidFlash()
{
try{
//PreviewCamera.Release();
var parameters = PreviewCamera.GetParameters();
parameters.FlashMode = global::Android.Hardware.Camera.Parameters.FlashModeOn;
PreviewCamera.SetParameters(parameters);
this.avviaViewAnteprimaFotocamera();
}catch(Exception e){
Console.WriteLine(e);
}
}
/// <summary>
/// Disattiva il flash
/// </summary>
public void de_activate_androidFlash()
{
try
{
//PreviewCamera.Release();
var parameters = PreviewCamera.GetParameters();
parameters.FlashMode = global::Android.Hardware.Camera.Parameters.FlashModeOff;
PreviewCamera.SetParameters(parameters);
this.avviaViewAnteprimaFotocamera();
}
catch (Exception e)
{Console.WriteLine(e);}
}
/// <summary>
/// Apre la fotocamera anteriore
/// </summary>
public void show_front_camera()
{
try
{
PreviewCamera.Release();
PreviewCamera = this.openCameraFacing(Android.Hardware.CameraFacing.Front);
this.SurfaceDestroyed(this.holder);
this.SurfaceChanged(this.holder, new Android.Graphics.Format(), previewSize.Width, previewSize.Height);
}
catch(Exception e)
{ Console.WriteLine(e);}
}
/// <summary>
/// Apre la fotocamera posteriore
/// </summary>
public void show_back_camera()
{
try
{
PreviewCamera.Release();
PreviewCamera = this.openCameraFacing(Android.Hardware.CameraFacing.Back);
this.SurfaceDestroyed(this.holder);
this.SurfaceChanged(this.holder, new Android.Graphics.Format(), previewSize.Width, previewSize.Height);
}
catch (Exception e)
{ Console.WriteLine(e); }
}
/// <summary>
/// cattura un immagine
/// </summary>
/// <returns>The picture.</returns>
public async Task<byte[]> take_picture()
{
pictureTaken = null;
try
{
this.PreviewCamera.StopPreview();
this.PreviewCamera.TakePicture(null, null, this);
this.PreviewCamera.StartPreview();
return pictureTaken;
/*var absolutePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim).AbsolutePath;
var folderPath = absolutePath + "/Camera";
var filePath = System.IO.Path.Combine(folderPath, string.Format("photo_{0}.jpg", Guid.NewGuid()));
var fileStream = new FileStream(filePath, FileMode.Create);
using (MemoryStream ms = new MemoryStream())
{
fileStream.CopyTo(ms);
fileStream.Close();
this.SurfaceDestroyed(this.holder);
this.SurfaceChanged(this.holder, new Android.Graphics.Format(), previewSize.Width, previewSize.Height);
return ms.ToArray();
}*/
}
catch(Exception e)
{ Console.WriteLine(e);}
return null;
}
/// <summary>
/// Evento che si attiva
/// </summary>
/// <param name="datxa">Data.</param>
/// <param name="camera">Camera.</param>
public void OnPictureTaken(byte[] data, Android.Hardware.Camera camera)
{
//camera.StartPreview();
pictureTaken = data;
Console.Write("jpeg data ready"); // Marcin controlla che siano pronti i dati quando il meotodo "take picture" ritorna la foto scattata"
}
/// <summary>
/// Apre e ritorna la camera orientata cercata
/// </summary>
/// <returns>Una camera del dispositivo cercata</returns>
/// <param name="orientazione">Posizione della camera che si vuole aprire.</param>
private Android.Hardware.Camera openCameraFacing(Android.Hardware.CameraFacing orientazione)
{
int cameraCount = 0;
Android.Hardware.Camera camera = null;
Android.Hardware.Camera.CameraInfo cameraInfo = new Android.Hardware.Camera.CameraInfo();
cameraCount = Android.Hardware.Camera.NumberOfCameras;
for (int camIdx = 0; camIdx < cameraCount; camIdx++)
{
Android.Hardware.Camera.GetCameraInfo(camIdx, cameraInfo);
if (cameraInfo.Facing == orientazione)
{
try
{
camera = Android.Hardware.Camera.Open(camIdx);
}
catch (Exception e)
{
Console.Write(e);
}
}
}
return camera;
}
#endregion
}
}
Я получаю эту ошибку в строке 316:
Java.Lang.RuntimeException: takePicture failed
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0
at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00089] in <7802aa64ad574c33adca332a3fa9706a>:0
at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeNonvirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0001f] in <7802aa64ad574c33adca332a3fa9706a>:0
at Android.Hardware.Camera.TakePicture (Android.Hardware.Camera+IShutterCallback shutter, Android.Hardware.Camera+IPictureCallback raw, Android.Hardware.Camera+IPictureCallback jpeg) [0x0007d] in /Users/builder/data/lanes/5945/dffc5912/source/monodroid/external/xamarin-android/src/Mono.Android/obj/Release/android-27/mcw/Android.Hardware.Camera.cs:4763
at Hangover.Droid.CustomRenderer.CameraPreview+<take_picture>d__23.MoveNext () [0x00026] in /Users/Giulio_Serra/HangoverCross/Hangover/Hangover.Android/CustomRenderer/CameraPreview.cs:316
--- End of managed Java.Lang.RuntimeException stack trace ---
java.lang.RuntimeException: takePicture failed
at android.hardware.Camera.native_takePicture(Native Method)
at android.hardware.Camera.takePicture(Camera.java:1488)
at android.hardware.Camera.takePicture(Camera.java:1433)
at md58432a647068b097f9637064b8985a5e0.ButtonRenderer_ButtonClickListener.n_onClick(Native Method)
at md58432a647068b097f9637064b8985a5e0.ButtonRenderer_ButtonClickListener.onClick(ButtonRenderer_ButtonClickListener.java:30)
at android.view.View.performClick(View.java:6259)
at android.view.View$PerformClick.run(View.java:24732)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6592)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)