如何使用 JNI 在 C 中获取原始 Android 相机缓冲区?

Posted

技术标签:

【中文标题】如何使用 JNI 在 C 中获取原始 Android 相机缓冲区?【英文标题】:How do I get the raw Android camera buffer in C using JNI? 【发布时间】:2012-05-20 05:14:45 【问题描述】:

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

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

【问题讨论】:

您只有普通权限还是任何额外(可能是 root)权限? 你有没有找到一种方法来做到这一点。因为我正在寻找同样的东西。 【参考方案1】:

我对主题进行了一些调查。这个presentation(来自p.277,中文)帮助很大。

相机预览调用链

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

    用户调用Camera.startPreview(),这是一个原生函数。 android_hardware_Camera_startPreview 调用 C++ 的 startPreview 方法 Camera 类。 Camera调用ICamera接口的startPreview方法 ICamera 向远程客户端发出 IPC 调用。 它调用CameraService类的setCameraMode方法。 CameraService 设置一个窗口来显示预览并调用CameraHardwareInterface 类的startPreview 方法。 后者试图在特定的camera_device_t 设备上调用start_preview 方法。 我没有进一步查找,但它应该会调用驱动程序。 当图像到达时,dataCallbackCameraService 被调用。 它将数据传递给客户端的handlePreviewData方法。 客户端要么复制缓冲区,要么直接将其发送到ICameraClientICameraClient 将其通过IPC 发送到CameraCamera 调用已注册的侦听器并将缓冲区传递给 JNI。 它调用 Java 类中的回调。如果用户提供了带有Camera.addCallbackBuffer 的缓冲区,则它首先复制到缓冲区。 最后,Java 类Camera 处理消息并调用Camera.PreviewCallbackonPreviewFrame 方法。

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

预览表面

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

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

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

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

但是这个 API 是隐藏的。请参阅 this question 了解使用方法。

【讨论】:

这个出色的总结有两个方面。 1. 步骤 10-11可能涉及memcpy,但很可能它们只通过缓冲区而不复制,通过使用进程间内存共享。 2. Surfacescreenshot 方法是复制像素,还涉及到IPC,可能还有颜色转换,所以为了更好的性能使用它是错误的. 2a. 这个screenshot 方法产生一个RGB 位图,而大多数视频编码需要YUV。【参考方案2】:

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

想必OpenCV等人翻阅了Android框架原生代码,绕过了Java Camera API,直接与下面的服务对话。

这是相当危险的,因为绝对不能保证这些接口不会在 Android 版本之间发生变化,因为它们不是公共 API 的一部分。现在使用它们可能没问题,然后当用户升级他们的设备时,您的应用将停止工作。

【讨论】:

【参考方案3】:

你看过OpenCV for Android。他们的advanced tutorials 展示了如何使用 JNI,并且他们的相机包中有一个 NativeProcessor 对象。

【讨论】:

以上是关于如何使用 JNI 在 C 中获取原始 Android 相机缓冲区?的主要内容,如果未能解决你的问题,请参考以下文章

Android:JNI与NDKNDK构建的脚本文件配置

Android 如何在jni层使用Looper

Android 如何在jni层使用Looper

如何使用 JNI 从本机 c 库将 double 和 unsigned int 返回到 java

JNI签名与数据匹配

通过 JNI (Cocos2dx) 获取 Java 对象到 C++