Xamarin Cam2 IOnImageAvailableListener的OnImageAvailable被调用两次导致

时间:2018-08-15 17:56:47

标签: xamarin xamarin.android android-camera2

更新:最初的问题已被回答,为什么会发生崩溃,但仍然存在着一个挥之不去的问题,即为什么调用'OnImageAvailable'回调可能会如此?调用它时,我想对图像进行处理,但是那时我运行的任何方法都会被调用很多次。这是使用生成图像的地方吗?

  

我正在使用here中的示例代码来实现Android Camera2 API的Xamarin Android实现。我的问题是,一次按下捕获按钮时,OnCameraAvalibleListener的{​​{1}}回调会被多次调用。

这引起了一个问题,因为OnImageAvailable中的图像需要先关闭,然后才能使用,但直到AcquireNextImage类的Run方法才调用close下面。

这会导致以下两个错误:

  

无法获取缓冲物料,很可能客户尝试获取   超过maxImages缓冲区

AND

  

maxImages(2)已经被获取,在获取前调用#close   更多。

最大图像默认设置为2,但是将其设置为1则无济于事。如何防止回调被调用两次?

ImageSaver

2 个答案:

答案 0 :(得分:1)

如果管道中还有其他图片,则一旦离开就可以再次调用方法OnImageAvailable

我建议使用与调用Close相同的方法来调用AcquireNextImage。因此,如果您选择直接从该回调中获取图像,则也必须在其中调用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();
    }
}

答案 1 :(得分:0)

我也面临这个问题了很长时间,并尝试实现@kzrytof的解决方案,但是并没有获得预期的帮助,但是找到了使onImageAvailable执行一次的方法。

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

我知道,您的问题是针对xamarin的,而我的以下代码位于本机android java中,但方法和功能相同,因此请尝试一次:

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