如何使用 JNI 以 C 语言获取原始 Android 相机缓冲区?

2022-09-01 19:10:51

我一直在详尽地搜索Google和StackOverflow,但找不到这个。也许我错过了一些明显的东西。谢谢!

(这是因为预览回调的 Java 实现(即使使用缓冲区)效率太低。


答案 1

我对这个话题做了一些调查。这个演讲(来自第277页,中文)帮助很大。

相机预览呼叫链

正如其他人所提到的,您可以使用方法获取缓冲区。
以下是它在那里发生的方式(详细版本):Camera.setPreviewCallback

  1. 用户调用,这是一个本机函数。Camera.startPreview()
  2. android_hardware_Camera_startPreview调用C++类的方法。startPreviewCamera
  3. Camera调用接口的方法startPreviewICamera
  4. ICamera调用远程客户端。IPC
  5. 它调用类的方法。setCameraModeCameraService
  6. CameraService设置一个窗口以显示预览并调用类的方法。startPreviewCameraHardwareInterface
  7. 后者尝试在特定设备上调用方法。
    我没有进一步查找,但它应该对驱动程序执行呼叫。start_previewcamera_device_t
  8. 当图像到达时,将调用 of。dataCallbackCameraService
  9. 它将数据传递给客户端的方法。handlePreviewData
  10. 客户端复制缓冲区或将其直接发送到 .ICameraClient
  11. ICameraClient将其发送到 .IPCCamera
  12. Camera调用已注册的侦听器并将缓冲区传递给 。JNI
  13. 它在 Java 类中调用回调。如果用户提供了一个缓冲区,则它首先复制到该缓冲区。Camera.addCallbackBuffer
  14. 最后,Java 类处理消息并调用 .CameraonPreviewFrameCamera.PreviewCallback

如您所见,在步骤 10、11 中调用了 2 次调用,并且至少复制了两次缓冲区。返回的原始缓冲区的第一个实例托管在另一个进程中,由于 中的安全检查,您无法访问它。IPCcamera_device_tCameraService

预览图面

但是,当您使用任一设置预览图面时,或者直接将其传递到相机设备并实时刷新,而无需上述所有链的参与。正如它的文档所说:Camera.setPreviewTextureCamera.setPreviewDisplay

处理到由屏幕合成器管理的原始缓冲区上。

Java类有一个方法来检索它的内容:Surface

public static native Bitmap screenshot(int width, int height, int minLayer, int maxLayer);

但是这个 API 是隐藏的。请参阅即此问题以获取使用它的方法。


答案 2

没有公共API可以做你想做的事;唯一的官方(即保证工作)方法是通过调用Camera.setPreviewCallback()设置的Java级预览回调。在Android > 3.0中,您还可以使用Camera.setPreviewTexture()将预览数据路由到GPU,并使用GLES对其进行处理(或将其读回CPU)。GPU 路径是 ICS AOSP 相机应用程序用于其视频效果的路径。

据推测,OpenCV和其他人已经查看了Android框架的原生代码,并绕过了Java Camera API,直接与下面的服务进行了交谈。

这是相当危险的,因为绝对不能保证这些接口不会在Android版本之间更改,因为它们不是公共API的一部分。现在使用它们可能很好,然后当用户升级其设备时,你的应用将停止工作。


推荐