本开发者指南将逐步介绍如何让您的应用在通过 Android Camera2 API 对摄像头的专属控制权和与 ARCore 共享摄像头访问权限之间无缝切换。
本主题假设您:
已完成 ARCore 快速入门
熟悉 Android Camera2 API(如需了解详情,请参阅特定于 Android 的 Camera2 示例)
构建和运行示例应用
当您构建并运行 Shared Camera Java 示例应用时,它会创建一个支持共享摄像头访问权限的 ARCore 会话。应用在非 AR 模式下启动,并且 ARCore 暂停。
当应用在非 AR 模式下运行时,相机查看器会显示深褐色颜色效果。切换到 AR 模式后,当应用通过恢复暂停的会话将摄像头控制权交还给 ARCore 时,棕褐色效果会关闭。
您可以使用应用中的 AR 开关来更改模式。在预览期间,两种模式都会显示 Camera2 拍摄的连续帧的数量。
如需构建并运行共享相机 Java 示例应用,请执行以下操作:
下载并解压缩 Google ARCore SDK for Android。
打开
samples/shared_camera_java
项目。确保您的 Android 设备通过 USB 连接到开发机器。如需了解详细信息,请参阅 ARCore 支持的设备。
在 Android Studio 中,点击 Run 。
选择您的设备作为部署目标,然后点击 OK 以在设备上启动示例应用。
在设备上,确认您允许应用拍摄照片和录制视频。
如果系统提示您更新,请更新或安装最新版本的 ARCore。
使用 AR 开关可在非 AR 模式和 AR 模式之间切换。
允许应用与 ARCore 共享摄像头访问权限的概览
请按照以下步骤在您的应用中使用 ARCore 实现共享摄像头访问权限。所有代码段均可在 shared_camera_java
示例的 SharedCameraActivity.java
中找到。
请求 CAMERA
权限
为了能够使用设备的相机,用户必须向您的应用授予 CAMERA
权限。ARCore 示例包含一个 CameraPermissionHelper
,它提供了用于为您的应用请求正确权限的实用程序。
Java
protected void onResume() {
// Request the camera permission, if necessary.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this);
}
}
Kotlin
override fun onResume() {
// Request the camera permission, if necessary.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this)
}
}
确保 ARCore 已安装并且是最新版本
必须先安装 ARCore 并将其更新为最新版本,然后才能使用。以下代码段展示了如何请求安装 ARCore(如果 ARCore 尚未安装在设备上)。
Java
boolean isARCoreSupportedAndUpToDate() {
// Make sure that ARCore is installed and supported on this device.
ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(this);
switch (availability) {
case SUPPORTED_INSTALLED:
return true;
case SUPPORTED_APK_TOO_OLD:
case SUPPORTED_NOT_INSTALLED:
// Requests an ARCore installation or updates ARCore if needed.
ArCoreApk.InstallStatus installStatus = ArCoreApk.getInstance().requestInstall(this, userRequestedInstall);
switch (installStatus) {
case INSTALL_REQUESTED:
return false;
case INSTALLED:
return true;
}
return false;
default:
// Handle the error. For example, show the user a snackbar that tells them
// ARCore is not supported on their device.
return false;
}
}
Kotlin
// Determine ARCore installation status.
// Requests an ARCore installation or updates ARCore if needed.
fun isARCoreSupportedAndUpToDate(): Boolean {
when (ArCoreApk.getInstance().checkAvailability(this)) {
Availability.SUPPORTED_INSTALLED -> return true
Availability.SUPPORTED_APK_TOO_OLD,
Availability.SUPPORTED_NOT_INSTALLED -> {
when(ArCoreApk.getInstance().requestInstall(this, userRequestedInstall)) {
InstallStatus.INSTALLED -> return true
else -> return false
}
}
else -> {
// Handle the error. For example, show the user a snackbar that tells them
// ARCore is not supported on their device.
return false
}
}
}
创建支持摄像头共享的 ARCore 会话
这涉及创建会话以及存储 ARCore 共享摄像头的引用和 ID:
Java
// Create an ARCore session that supports camera sharing.
sharedSession = new Session(this, EnumSet.of(Session.Feature.SHARED_CAMERA))
// Store the ARCore shared camera reference.
sharedCamera = sharedSession.getSharedCamera();
// Store the ID of the camera that ARCore uses.
cameraId = sharedSession.getCameraConfig().getCameraId();
Kotlin
// Create an ARCore session that supports camera sharing.
sharedSession = Session(this, EnumSet.of(Session.Feature.SHARED_CAMERA))
// Store the ARCore shared camera reference.
sharedCamera = sharedSession.sharedCamera
// Store the ID of the camera that ARCore uses.
cameraId = sharedSession.cameraConfig.cameraId
(可选)告知 ARCore 有任何自定义 surface
请求额外的自定义 surface 会增加设备的性能需求。为了确保运行良好,请在用户将使用的设备上测试您的应用。
默认情况下,ARCore 会请求两个视频流:
- 1x YUV CPU 流,目前始终为
640x480
。
ARCore 会将此数据流用于动作跟踪。 - 1x GPU 流,通常为
1920x1080
使用Session#getCameraConfig()
确定当前的 GPU 流分辨率。
您可以使用 getSupportedCameraConfigs()
和 setCameraConfig()
更改受支持设备上的 GPU 流的分辨率。
从粗略的角度来看,您应该了解:
设备类型 | 支持同时在线播放 |
---|---|
高端手机 |
|
中端手机 |
|
如需使用自定义 surface(例如 CPU 图片读取器 surface),请务必将其添加到需要更新的 surface(例如 ImageReader
)列表中。
Java
sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));
Kotlin
sharedCamera.setAppSurfaces(this.cameraId, listOf(imageReader.surface))
打开相机
使用 ARCore 封装的回调打开摄像头:
Java
// Wrap the callback in a shared camera callback.
CameraDevice.StateCallback wrappedCallback =
sharedCamera.createARDeviceStateCallback(cameraDeviceCallback, backgroundHandler);
// Store a reference to the camera system service.
cameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
// Open the camera device using the ARCore wrapped callback.
cameraManager.openCamera(cameraId, wrappedCallback, backgroundHandler);
Kotlin
// Wrap the callback in a shared camera callback.
val wrappedCallback = sharedCamera.createARDeviceStateCallback(cameraDeviceCallback, backgroundHandler)
// Store a reference to the camera system service.
val cameraManager = this.getSystemService(Context.CAMERA_SERVICE) as CameraManager
// Open the camera device using the ARCore wrapped callback.
cameraManager.openCamera(cameraId, wrappedCallback, backgroundHandler)
使用相机设备状态回调
在相机设备状态回调中,存储对相机设备的引用,并启动新的拍摄会话。
Java
public void onOpened(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " opened.");
SharedCameraActivity.this.cameraDevice = cameraDevice;
createCameraPreviewSession();
}
Kotlin
fun onOpened(cameraDevice: CameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.id + " opened.")
this.cameraDevice = cameraDevice
createCameraPreviewSession()
}
创建新的拍摄会话
构建新的拍摄请求。使用 TEMPLATE_RECORD
可确保捕获请求与 ARCore 兼容,并允许在运行时在非 AR 模式和 AR 模式之间无缝切换。
Java
void createCameraPreviewSession() {
try {
// Create an ARCore-compatible capture request using `TEMPLATE_RECORD`.
previewCaptureRequestBuilder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
// Build a list of surfaces, starting with ARCore provided surfaces.
List<Surface> surfaceList = sharedCamera.getArCoreSurfaces();
// (Optional) Add a CPU image reader surface.
surfaceList.add(cpuImageReader.getSurface());
// The list should now contain three surfaces:
// 0. sharedCamera.getSurfaceTexture()
// 1. …
// 2. cpuImageReader.getSurface()
// Add ARCore surfaces and CPU image surface targets.
for (Surface surface : surfaceList) {
previewCaptureRequestBuilder.addTarget(surface);
}
// Wrap our callback in a shared camera callback.
CameraCaptureSession.StateCallback wrappedCallback =
sharedCamera.createARSessionStateCallback(cameraSessionStateCallback, backgroundHandler);
// Create a camera capture session for camera preview using an ARCore wrapped callback.
cameraDevice.createCaptureSession(surfaceList, wrappedCallback, backgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "CameraAccessException", e);
}
}
Kotlin
fun createCameraPreviewSession() {
try {
// Create an ARCore-compatible capture request using `TEMPLATE_RECORD`.
previewCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
// Build a list of surfaces, starting with ARCore provided surfaces.
val surfaceList: MutableList<Surface> = sharedCamera.arCoreSurfaces
// (Optional) Add a CPU image reader surface.
surfaceList.add(cpuImageReader.getSurface())
// The list should now contain three surfaces:
// 0. sharedCamera.getSurfaceTexture()
// 1. …
// 2. cpuImageReader.getSurface()
// Add ARCore surfaces and CPU image surface targets.
for (surface in surfaceList) {
previewCaptureRequestBuilder.addTarget(surface)
}
// Wrap the callback in a shared camera callback.
val wrappedCallback = sharedCamera.createARSessionStateCallback(cameraSessionStateCallback, backgroundHandler)
// Create a camera capture session for camera preview using an ARCore wrapped callback.
cameraDevice.createCaptureSession(surfaceList, wrappedCallback, backgroundHandler)
} catch (e: CameraAccessException) {
Log.e(TAG, "CameraAccessException", e)
}
}
以非 AR 或 AR 模式启动
如需开始捕获帧,请从相机拍摄会话 onConfigured()
状态回调中调用 captureSession.setRepeatingRequest()
。在 onActive()
回调中恢复 ARCore 会话,以 AR 模式启动。
Java
// Repeating camera capture session state callback.
CameraCaptureSession.StateCallback cameraSessionStateCallback =
new CameraCaptureSession.StateCallback() {
// Called when ARCore first configures the camera capture session after
// initializing the app, and again each time the activity resumes.
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
captureSession = session;
setRepeatingCaptureRequest();
}
@Override
public void onActive(@NonNull CameraCaptureSession session) {
if (arMode && !arcoreActive) {
resumeARCore();
}
}
};
// A repeating camera capture session capture callback.
CameraCaptureSession.CaptureCallback cameraCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(…) {
shouldUpdateSurfaceTexture.set(true);
}
};
void setRepeatingCaptureRequest() {
captureSession.setRepeatingRequest(
previewCaptureRequestBuilder.build(), cameraCaptureCallback, backgroundHandler);
}
void resumeARCore() {
// Resume ARCore.
sharedSession.resume();
arcoreActive = true;
// Set the capture session callback while in AR mode.
sharedCamera.setCaptureCallback(cameraCaptureCallback, backgroundHandler);
}
Kotlin
val cameraSessionStateCallback = object : CameraCaptureSession.StateCallback() {
// Called when ARCore first configures the camera capture session after
// initializing the app, and again each time the activity resumes.
override fun onConfigured(session: CameraCaptureSession) {
captureSession = session
setRepeatingCaptureRequest()
}
override fun onActive(session: CameraCaptureSession) {
if (arMode && !arcoreActive) {
resumeARCore()
}
}
}
val cameraCaptureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
shouldUpdateSurfaceTexture.set(true);
}
}
fun setRepeatingCaptureRequest() {
captureSession.setRepeatingRequest(
previewCaptureRequestBuilder.build(), cameraCaptureCallback, backgroundHandler
)
}
fun resumeARCore() {
// Resume ARCore.
sharedSession.resume()
arcoreActive = true
// Set the capture session callback while in AR mode.
sharedCamera.setCaptureCallback(cameraCaptureCallback, backgroundHandler)
}
在运行时在非 AR 或 AR 模式之间无缝切换
如需从非 AR 模式切换到 AR 模式并恢复暂停的 ARCore 会话,请执行以下操作:
Java
// Resume the ARCore session.
resumeARCore();
Kotlin
// Resume the ARCore session.
resumeARCore()
如需从 AR 模式切换到非 AR 模式,请执行以下操作:
Java
// Pause ARCore.
sharedSession.pause();
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();
Kotlin
// Pause ARCore.
sharedSession.pause()
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()