通过 ARCore 共享相机访问权限

本开发者指南逐步介绍了如何为应用启用 通过 Android Camera2 API 以及如何与 ARCore 共享摄像头使用权限。

本主题假定您满足以下条件:

构建和运行示例应用

当您构建并运行共享相机 Java 示例应用时,它会创建一个 支持共享摄像头访问权限的 ARCore 会话。应用在非 AR 模式下启动 并且 ARCore 已暂停。

当应用在非 AR 模式下运行时,相机查看器会显示深褐色 效果。切换到 AR 模式时,棕褐色效果会随着应用而关闭 通过恢复暂停的会话将摄像头控制返回给 ARCore。

您可以使用应用中的 AR 开关更改模式。在预览期间,这两种模式 显示 Camera2 拍摄的连续帧数。

如需构建并运行共享相机 Java 示例应用,请执行以下操作:

  1. 下载并解压缩 Google ARCore SDK for Android

  2. 打开 samples/shared_camera_java 项目。

  3. 确保您的 Android 设备已连接到开发机器。 通过 USB。请参阅 ARCore 支持的设备 了解详情。

  4. 在 Android Studio 中,点击 Run

  5. 选择您的设备作为部署目标,然后点击 OK 以启动 示例应用。

  6. 在设备上,确认您要允许该应用拍摄照片并 录制视频。

  7. 如果系统提示您更新,请更新或安装最新版本的 ARCore。

  8. 使用 AR 开关在非 AR 和 AR 模式之间切换。

允许应用与 ARCore 共享摄像头使用权限的概览

按照以下步骤在您的应用中与 ARCore 实现共享摄像头访问权限。 所有代码段都可在 SharedCameraActivity.javashared_camera_java内 示例。

请求 CAMERA 权限

为了能够使用设备的相机,用户 必须向您的应用授予 CAMERA 权限。 ARCore 示例包括一个 CameraPermissionHelper, 该类提供了可为您的应用请求正确权限的实用程序。

JavaKotlin
protected void onResume() {
 
// Request the camera permission, if necessary.
 
if (!CameraPermissionHelper.hasCameraPermission(this)) {
     
CameraPermissionHelper.requestCameraPermission(this);
 
}
}
override fun onResume() {
 
// Request the camera permission, if necessary.
 
if (!CameraPermissionHelper.hasCameraPermission(this)) {
   
CameraPermissionHelper.requestCameraPermission(this)
 
}
}

确保 ARCore 已安装并且是最新版本

ARCore 必须安装为最新版本才能使用。 以下代码段展示了如何请求安装 ARCore(如果尚未安装在设备上)。

JavaKotlin
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;
 
}
}
// 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 共享摄像头:

JavaKotlin
// 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();
// 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 将请求两个视频流:

  1. 1x YUV CPU 流,目前始终为 640x480
    ARCore 使用此数据流进行运动跟踪
  2. 一个 1x GPU 流,通常为 1920x1080
    使用 Session#getCameraConfig() 以确定当前的 GPU 流分辨率。

您可以使用 getSupportedCameraConfigs()setCameraConfig()

作为粗略的指标,您可以获得:

设备类型 支持同时在线播放
高端手机
  • 2x YUV CPU 流,例如640x4801920x1080
  • 1x GPU 流,例如1920x1080
  • 1x 偶尔的高分辨率静态图片 (JPEG),例如12MP
中端手机
  • 2x YUV CPU 流,例如640x4801920x1080
  • 1x GPU 流,例如1920x1080
。 –或– <ph type="x-smartling-placeholder">
    </ph>
  • 1x YUV CPU 流,例如640x480 – 或 – 1920x1080
  • 1x GPU 流,例如1920x1080
  • 1x 偶尔的高分辨率静态图片 (JPEG),例如12MP

如需使用自定义 surface(例如 CPU 图片读取器 surface),请务必添加该 surface 添加到需要更新的 surface 列表中 (例如 ImageReader)。

JavaKotlin
sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));
sharedCamera.setAppSurfaces(this.cameraId, listOf(imageReader.surface))

打开相机

使用 ARCore 封装的回调打开相机:

JavaKotlin
// 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);
// 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)

使用相机设备状态回调

在相机设备状态回调中存储对相机设备的引用,以及 启动新的拍摄会话。

JavaKotlin
public void onOpened(@NonNull CameraDevice cameraDevice) {
   
Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " opened.");
   
SharedCameraActivity.this.cameraDevice = cameraDevice;
    createCameraPreviewSession
();
}
fun onOpened(cameraDevice: CameraDevice) {
 
Log.d(TAG, "Camera device ID " + cameraDevice.id + " opened.")
 
this.cameraDevice = cameraDevice
  createCameraPreviewSession
()
}

创建新的拍摄会话

构建新的拍摄请求。使用TEMPLATE_RECORD 以确保捕获请求与 ARCore 兼容, 在运行时在非 AR 和 AR 模式之间切换。

JavaKotlin
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);
 
}
}
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 模式开始

如需开始捕获帧,请调用 captureSession.setRepeatingRequest() 从相机拍摄会话 onConfigured() 状态回调中获取。 在 onActive() 回调中恢复 ARCore 会话,以在 AR 模式下启动。

JavaKotlin
// 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);
}
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 会话,请执行以下操作:

JavaKotlin
// Resume the ARCore session.
resumeARCore
();
// Resume the ARCore session.
resumeARCore
()

如需从 AR 模式切换到非 AR 模式,请执行以下操作:

JavaKotlin
// Pause ARCore.
sharedSession
.pause();

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest
();
// Pause ARCore.
sharedSession
.pause()

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest
()