Xamarin Cam2 Ion ImageAvailableListener';s OnImageAvailable调用两次导致

Xamarin Cam2 Ion ImageAvailableListener';s OnImageAvailable调用两次导致,xamarin,xamarin.android,android-camera2,Xamarin,Xamarin.android,Android Camera2,更新:最初的问题已经得到了回答,为什么会发生崩溃,但挥之不去的问题仍然是为什么“OnImageAvailable”回调调用如此之多的时间?当它被调用时,我想处理图像,但无论我当时运行什么方法,都会被多次调用。这是使用结果图像的错误位置吗 我使用的是Android Camera2 API的Xamarin Android实现的示例代码。我的问题是,当按下捕获按钮一次时,onCameraAvailableListener的OnImageAvailable回调会被多次调用 这导致了一个问题,因为需要先

更新:最初的问题已经得到了回答,为什么会发生崩溃,但挥之不去的问题仍然是为什么“OnImageAvailable”回调调用如此之多的时间?当它被调用时,我想处理图像,但无论我当时运行什么方法,都会被多次调用。这是使用结果图像的错误位置吗


我使用的是Android Camera2 API的Xamarin Android实现的示例代码。我的问题是,当按下捕获按钮一次时,
onCameraAvailableListener
OnImageAvailable
回调会被多次调用

这导致了一个问题,因为需要先关闭
AcquireNextImage
中的图像,然后才能使用另一个图像,但是直到
ImageSaver
类的
Run
方法才能调用close,如下所示

这会导致以下两个错误:

无法获取缓冲区项,很可能是客户端试图获取缓冲区项 超过最大值缓冲区

已获取最大值(2),请在获取前调用#关闭 更多

默认情况下,“最大图像”设置为2,但将其设置为1没有帮助。如何防止回调被调用两次

public void OnImageAvailable(ImageReader reader)
    {
        var image = reader.AcquireNextImage();
        owner.mBackgroundHandler.Post(new ImageSaver(image, file));
    }

    // Saves a JPEG {@link Image} into the specified {@link File}.
    private class ImageSaver : Java.Lang.Object, IRunnable
    {
        // The JPEG image
        private Image mImage;

        // The file we save the image into.
        private File mFile;

        public ImageSaver(Image image, File file)
        {
            if (image == null)
                throw new System.ArgumentNullException("image");
            if (file == null)
                throw new System.ArgumentNullException("file");

            mImage = image;
            mFile = file;
        }

        public void Run()
        {
            ByteBuffer buffer = mImage.GetPlanes()[0].Buffer;
            byte[] bytes = new byte[buffer.Remaining()];
            buffer.Get(bytes);
            using (var output = new FileOutputStream(mFile))
            {
                try
                {
                    output.Write(bytes);
                }
                catch (IOException e)
                {
                    e.PrintStackTrace();
                }
                finally
                {
                    mImage.Close();
                }
            }
        }
    }

如果管道中还有另一张图片,则只要您离开该方法,就可以再次调用该方法
OnImageAvailable

我建议您使用与调用AcquireNextImage相同的方法调用
Close
。因此,如果您选择直接从该回调获取图像,那么您也必须在其中调用
Close

一个解决方案是用这种方法抓取图像并立即关闭

public void OnImageAvailable(ImageReader reader)
{
    var image = reader.AcquireNextImage();

    try
    {
        ByteBuffer buffer = mImage.GetPlanes()[0].Buffer;
        byte[] bytes = new byte[buffer.Remaining()];
        buffer.Get(bytes);

        // I am not sure where you get the file instance but it is not important.
        owner.mBackgroundHandler.Post(new ImageSaver(bytes, file));
    }
    finally
    {
        image.Close();
    }
}
ImageSaver
将被修改为接受字节数组作为构造函数中的第一个参数:

public ImageSaver(byte[] bytes, File file)
{
    if (bytes == null)
        throw new System.ArgumentNullException("bytes");
    if (file == null)
        throw new System.ArgumentNullException("file");

    mBytes = bytes;
    mFile = file;
}
此解决方案的主要缺点是,在图像一个接一个地被处理之前,基本上会将图像保存在内存中,这会给内存带来很大压力

另一个解决方案是在背景线程上获取图像

public void OnImageAvailable(ImageReader reader)
{
    // Again, I am not sure where you get the file instance but it is not important.
    owner.mBackgroundHandler.Post(new ImageSaver(reader, file));
}
此解决方案对内存的占用较少;但是,根据您的需要,您可能必须将图像的最大数量从2个增加到更高的数量。同样,需要修改
ImageSaver
的构造函数,以接受
ImageReader
作为参数:

public ImageSaver(ImageReader imageReader, File file)
{
    if (imageReader == null)
        throw new System.ArgumentNullException("imageReader");
    if (file == null)
        throw new System.ArgumentNullException("file");

    mImageReader = imageReader;
    mFile = file;
}
现在,
Run
方法将负责获取和发布图像:

public void Run()
{
    Image image = mImageReader.AcquireNextImage();

    try
    {
        ByteBuffer buffer = image.GetPlanes()[0].Buffer;
        byte[] bytes = new byte[buffer.Remaining()];
        buffer.Get(bytes);

        using (var output = new FileOutputStream(mFile))
        {
            try
            {
                output.Write(bytes);
            }
            catch (IOException e)
            {
                e.PrintStackTrace();
            }
        }
    }
    finally
    {
        image?.Close();
    }
}

我也花了更长的时间来面对这个问题,并尝试实现@kzrytof的解决方案,但效果并没有达到预期,而是找到了让onImageAvailable执行一次的方法

场景:当图像可用时,调用onImageAvailable方法,对吗? 因此,我所做的是在使用image.close()关闭图像之后;我调用了imagereader.setonImageAvailableListener()并使侦听器=null。这样,我第二次停止了执行。,

我知道,您的问题是针对xamarin的,我下面的代码是用原生android java编写的,但方法和功能是相同的,所以请尝试一次:


非常好的回答,Kzrystof。非常感谢。请注意下面的问题。@Sevren只要有一个新的图像可用,回调方法就会被调用。好的,在它将多个图像写入同一个文件的磁盘后,我可以从哪里简单地获得我需要的一张图片?如果它被保存到磁盘,那么回叫在哪里说“好的,我完成了,使用这个图像”?我真的对这个嘲笑API和我为了得到一张照片而必须经历的所有胡说八道感到失望。@Sevren如果你只是想要一张静态照片,你可能想查看这个文件。这也是我想使用的一个,但每次拍摄一张照片时,它也会被调用很多次,比如10或15次。
@Override
        public void onImageAvailable(ImageReader reader) {
            final Image image=imageReader.acquireLatestImage();
        try {
            if (image != null) {
                Image.Plane[] planes = image.getPlanes();
                ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * width;
                int bitmapWidth = width + rowPadding / pixelStride;

                if (latestBitmap == null ||
                        latestBitmap.getWidth() != bitmapWidth ||
                        latestBitmap.getHeight() != height) {
                    if (latestBitmap != null) {
                        latestBitmap.recycle();
                    }

                  }

                latestBitmap.copyPixelsFromBuffer(buffer);
            }
        }
            catch(Exception e){

            }
            finally{
                image.close();
                imageReader.setOnImageAvailableListener(null, svc.getHandler());
            }
    // next steps to save the image
}