简体   繁体   中英

Error trying to take picture with Android.Hardware.Camera

I'm trying to make a custom renderer in my Xamarin.Form App to display the Camera Stream, iOS works just fine, but android is making me cry , At this point i don't know what to do.

The camera preview works just fine and I'm even able to switch between front camera and back camera, but I'm not able to take a picture, the whole camera is streamed inside a SurfaceView following this tutorial: https://docs.microsoft.com/it-it/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view

here is my code:

    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
    }
}

I keep getting this error at line 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)

Here is the new method that is invoked by the pressing of the button:

 /// <summary>
    /// cattura un immagine
    /// </summary>
    /// <returns>The picture.</returns>
    public async Task<byte[]> take_picture()
    {
        pictureTaken = null;

        try
        {
            this.PreviewCamera.StopPreview();
            this.PreviewCamera.TakePicture(new ShutterCameraCallback(),new RawCameraCallback(),new JpegCameraCallback());
            this.PreviewCamera.StartPreview();
            return pictureTaken;

        }
        catch(Exception e)
        { Console.WriteLine(e);}

        return null;
    }

Here are the three classes:

    using System;
using static Android.Hardware.Camera;

namespace Hangover.Droid.CameraCallbacks
{
    public class ShutterCameraCallback : Java.Lang.Object, IShutterCallback
    {
        public void OnShutter()
        {
            Console.Write("shutter");
        }
    }
}

second one:

using System;
using Android.Hardware;
using Java.Lang;
using static Android.Hardware.Camera;

namespace Hangover.Droid.CameraCallbacks
{
    public class RawCameraCallback : Java.Lang.Object ,IPictureCallback 
    {

        public void OnPictureTaken(byte[] data, Android.Hardware.Camera camera)
        {
            Console.Write("raw data");
            Console.Write(data);
        }
    }
}

The third:

using System;
using static Android.Hardware.Camera;

namespace Hangover.Droid.CameraCallbacks
{
    public class ShutterCameraCallback : Java.Lang.Object, IShutterCallback
    {
        public void OnShutter()
        {
            Console.Write("shutter");
        }
    }
}

Still no luck , getting the same old error:

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM