29

It is 2017 and I am finally starting switching from Camera1 to Camera2. In Camera1 I was greatly relying on setPreviewCallbackWithBuffer() to perform a real time frame processing, however in Camera2 this works much much slower to the point where it becomes almost unusable.

To compare, on Moto G3 Camera1 can easily produce 30-40 FPS while on Camera2 I couldn't get more than 10-15 FPS.

Here is how I am creating ImageReader

imageReader = ImageReader
  .newInstance(
    previewSize.width,        // size is around 1280x720
    previewSize.height,
    ImageFormat.YUV_420_888,  // note, it is not JPEG
    2 // max number of images, does not really affect performance
  );

imageReader.setOnImageAvailableListener(
  callback,
  CameraThread.getInstance().createHandler()
);

Callback itself does the minimum possible job:

Image image = reader.acquireNextImage();
image.close();

I already checked similar answers, such as this one. However their problem is that they're using JPEG image format instead of YUV_420_888.

How to achieve a performance similar to Camera1?

Community
  • 1
  • 1
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
  • the size of the ImageReader determines the output from the camera. You could also use `YV12` image format, plus make sure you have the latest version of the `Android API` – King Reload May 03 '17 at 10:35
  • @KingReload unlike YUV, YV12 is not supported by all devices. Moreover, I don't expect all customers to have the latest version of Android. If Camera1 works fine, why shouldn't Camera2 also work properly? – Dmitry Zaytsev May 03 '17 at 12:14
  • You could reduce the size of the image for the ```ImageReader``` so the preview could be smoother as said in this answer: http://stackoverflow.com/a/40152147/2949966 – ahasbini May 16 '17 at 08:44
  • @ahasbini it's true that frame rate will increase. However, I would like to have exactly the same preview frame resolution as I would have with Camera1. Otherwise, Camera2 would be a downgrade from capabilities of Camera1 and there would be no point in using it. – Dmitry Zaytsev May 16 '17 at 10:53
  • Hello, Dmitry. I am having the same problem. Have you solved it? Or return to Camera1? May be you tryed to use `setRepeatingBurst` instead `setRepeatingRequest`? – Maksim Kudimov Nov 16 '17 at 15:07
  • @MaximKudimov in Fotoapparat library we decided to support only Camera1. Camera2 so far proven to be even more unpredictable and harder to handle. – Dmitry Zaytsev Nov 16 '17 at 21:32
  • Did you find the solution? – nhoxbypass Mar 27 '18 at 11:07
  • @nhoxbypass unfortunately, no. We decided to abandon the Camera2 implementation and stick with Camera1 as it is more stable. – Dmitry Zaytsev Mar 27 '18 at 11:24

2 Answers2

3

I had the same performance problems on an app supporting both Camera1 and Camera2 APIs. When Android version was above Lollipop I used to switch to Camera2 API resulting in very bad performances (I had two target at time: an ImageReader and a Surface).

I ended up to use Camera2 API only when there was full Hardware support by the phone. You can check using the CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL

Hope it helps

Josef Grunig
  • 442
  • 4
  • 12
  • Just to clarify: do you mean that phones/devices with full Hardware support had better performance with Camera2 than phones without full Hardware support? – WillC May 03 '18 at 01:52
  • 1
    Yes its correct what you say. But the main problem actually was having 2 target at the same time (Surface and ImageReader) with Camera2 API on phones without full HW support (probably it was performing some kind of fallback on software somewhere). Having just one target like for preview was just fine in both APIs – Josef Grunig May 04 '18 at 07:39
2

This is just an observation but I'll post it anyway.

You say you are registering an OnImageAvailableListener. This listener does not deliver images but a reference to the same ImageReader you subscribed to. And then you must call either acquireLatestImage or acquireNextImage to get the actual image.

There is a paragraph in the docs that might be helpful to understand what is going on:

The image data is encapsulated in Image objects, and multiple such objects can be accessed at the same time, up to the number specified by the maxImages constructor parameter. New images sent to an ImageReader through its Surface are queued until accessed through the acquireLatestImage() or acquireNextImage() call. Due to memory limits, an image source will eventually stall or drop Images in trying to render to the Surface if the ImageReader does not obtain and release Images at a rate equal to the production rate.

So some things that might help:

  • request large memory in the manifest
  • Pass a large enough maxImages argument to the ImageReader constructor (You would get IllegalStateException if you exhaust the queue anyway).
  • Prefer acquireLatestImage over acquireNextImage for real-time processing. This method releases older images automatically while the other one does not, and thus using acquireNextImage by mistake will increasingly slowdown image delivery until you run out of memory.
Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • 3
    Thanks for your answer. Unfortunately I already tried all of that and it does not affect the performance even slightly (although I would expect some improvement from `maxImages`, there was in fact none). – Dmitry Zaytsev May 06 '17 at 17:37