Xamarin Cam2 IOnImageAvailableListener 的 OnImageAvailable 调用两次导致
Xamarin Cam2 IOnImageAvailableListener's OnImageAvailable called twice causing
更新:关于为什么会发生崩溃的最初问题已经得到解答,但挥之不去的问题仍然是为什么 'OnImageAvailable' 回调被调用如此多次?当它被调用时,我想对图像做一些事情,但是当时我 运行 的任何方法都被调用了很多次。这是使用结果图像的错误位置吗?
我正在使用找到的示例代码 here 用于 Android Camera2 API 的 Xamarin Android 实现。我的问题是,当单击捕获按钮时,OnCameraAvalibleListener
的 OnImageAvailable
回调会被多次调用。
这导致了一个问题,因为来自 AcquireNextImage
的图像需要先关闭才能使用另一个图像,但是直到 ImageSaver
的 Run
方法才会调用关闭 class 如下所示。
这会导致以下 2 个错误:
Unable to acquire a buffer item, very likely client tried to acquire
more than maxImages buffers
和
maxImages (2) has already been acquired, call #close before acquiring
more.
最大图像默认设置为 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() 并使 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
}
更新:关于为什么会发生崩溃的最初问题已经得到解答,但挥之不去的问题仍然是为什么 'OnImageAvailable' 回调被调用如此多次?当它被调用时,我想对图像做一些事情,但是当时我 运行 的任何方法都被调用了很多次。这是使用结果图像的错误位置吗?
我正在使用找到的示例代码 here 用于 Android Camera2 API 的 Xamarin Android 实现。我的问题是,当单击捕获按钮时,OnCameraAvalibleListener
的 OnImageAvailable
回调会被多次调用。
这导致了一个问题,因为来自 AcquireNextImage
的图像需要先关闭才能使用另一个图像,但是直到 ImageSaver
的 Run
方法才会调用关闭 class 如下所示。
这会导致以下 2 个错误:
Unable to acquire a buffer item, very likely client tried to acquire more than maxImages buffers
和
maxImages (2) has already been acquired, call #close before acquiring more.
最大图像默认设置为 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() 并使 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
}