與 ARCore 共用相機存取權

本開發人員指南將逐步說明如何讓應用程式透過 Android Camera2 API 專屬控制相機,並與 ARCore 共用相機存取權,兩者之間可無縫切換。

本主題假設您:

建構並執行範例應用程式

建構並執行 Shared Camera Java 範例應用程式時,系統會建立支援共用相機存取權的 ARCore 工作階段。應用程式會以非 AR 模式啟動,並暫停 ARCore。

應用程式在非 AR 模式下運作時,相機檢視畫面會顯示深褐色效果。切換至 AR 模式時,應用程式會恢復暫停的工作階段,將相機控制權交還給 ARCore,因此懷舊色調效果會關閉。

您可以在應用程式中使用 AR 切換鈕變更模式。預覽時,這兩種模式都會顯示 Camera2 擷取的連續影格數。

如要建構及執行 Shared Camera 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 實作攝影機共用存取權。 所有程式碼片段都位於 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

要求額外的自訂介面會增加裝置的效能需求。為確保應用程式正常運作,請在使用者會使用的裝置上測試應用程式。

ARCore 預設會要求兩個串流:

  1. 1 個 YUV CPU 串流,目前一律為 640x480
    ARCore 會使用這個串流進行動作追蹤
  2. 1 倍 GPU 串流,通常為 1920x1080
    使用 Session#getCameraConfig() 判斷目前的 GPU 串流解析度。

在支援的裝置上,您可以使用 getSupportedCameraConfigs()setCameraConfig() 變更 GPU 串流的解析度。

做為粗略指標,預期會發生以下情況:

裝置類型 支援同時串流
高階手機
  • 2 個 YUV CPU 串流,例如 640x4801920x1080
  • 1 個 GPU 串流,例如 1920x1080
  • 1 張偶爾出現的高解析度靜態圖片 (JPEG),例如 12MP
中階手機
  • 2 個 YUV CPU 串流,例如 640x4801920x1080
  • 1 個 GPU 串流,例如 1920x1080
–或–
  • 1 個 YUV CPU 串流,例如 640x4801920x1080
  • 1 個 GPU 串流,例如 1920x1080
  • 1 張偶爾出現的高解析度靜態圖片 (JPEG),例如 12MP

如要使用自訂介面 (例如 CPU 圖片讀取器介面),請務必將其新增至需要更新的介面清單 (例如 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()