Quyền truy cập vào máy ảnh dùng chung với ARCore

Hướng dẫn dành cho nhà phát triển này sẽ hướng dẫn bạn các bước để cho phép ứng dụng của bạn chuyển đổi liền mạch giữa quyền kiểm soát độc quyền camera thông qua Android Camera2 API và chia sẻ quyền truy cập camera với ARCore.

Chủ đề này giả định rằng bạn:

Xây dựng và chạy ứng dụng mẫu

Khi bạn tạo và chạy ứng dụng mẫu Shared Camera Java, ứng dụng này sẽ tạo một phiên ARCore hỗ trợ quyền truy cập vào camera dùng chung. Ứng dụng khởi động ở chế độ không phải thực tế tăng cường, với ARCore ở trạng thái tạm dừng.

Khi ứng dụng hoạt động ở chế độ không phải AR, trình xem camera sẽ hiển thị hiệu ứng màu nâu đỏ. Khi chuyển sang chế độ thực tế tăng cường, hiệu ứng nâu đỏ sẽ tắt khi ứng dụng trả lại quyền kiểm soát camera cho ARCore bằng cách tiếp tục phiên đã tạm dừng.

Bạn có thể dùng nút chuyển đổi AR trong ứng dụng để thay đổi chế độ. Trong quá trình xem trước, cả hai chế độ đều hiển thị số lượng khung hình liên tục mà Camera2 chụp được.

Cách tạo và chạy ứng dụng mẫu Shared Camera Java:

  1. Tải và trích xuất Google ARCore SDK cho Android.

  2. Mở dự án samples/shared_camera_java.

  3. Đảm bảo rằng thiết bị Android của bạn được kết nối với máy phát triển thông qua USB. Hãy xem danh sách Thiết bị được hỗ trợ của ARCore để biết thông tin chi tiết.

  4. Trong Android Studio, hãy nhấp vào Run .

  5. Chọn thiết bị của bạn làm mục tiêu triển khai rồi nhấp vào OK để chạy ứng dụng mẫu trên thiết bị.

  6. Trên thiết bị, hãy xác nhận rằng bạn muốn cho phép ứng dụng chụp ảnh và quay video.

  7. Nếu được nhắc, hãy cập nhật hoặc cài đặt phiên bản ARCore mới nhất.

  8. Dùng nút chuyển AR để chuyển đổi giữa chế độ không phải thực tế tăng cường và chế độ thực tế tăng cường.

Tổng quan về việc cho phép ứng dụng chia sẻ quyền truy cập camera với ARCore

Hãy làm theo các bước sau để triển khai quyền truy cập camera dùng chung bằng ARCore trong ứng dụng của bạn. Tất cả đoạn mã đều có trong SharedCameraActivity.java trong mẫu shared_camera_java.

Yêu cầu quyền CAMERA

Để có thể sử dụng camera của thiết bị, người dùng phải cấp cho ứng dụng của bạn quyền CAMERA. Các mẫu ARCore bao gồm CameraPermissionHelper, cung cấp các tiện ích để yêu cầu quyền phù hợp cho ứng dụng của bạn.

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)
  }
}

Đảm bảo bạn đã cài đặt và cập nhật ARCore

Bạn phải cài đặt và cập nhật ARCore thì mới có thể sử dụng. Đoạn mã sau đây cho biết cách yêu cầu cài đặt ARCore nếu ứng dụng này chưa được cài đặt trên thiết bị.

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
    }
  }
}

Tạo một phiên ARCore hỗ trợ tính năng chia sẻ camera

Việc này bao gồm tạo phiên và lưu trữ thông tin tham chiếu cũng như mã nhận dạng của camera dùng chung ARCore:

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

(Không bắt buộc) Thông báo cho ARCore về mọi bề mặt tuỳ chỉnh

Việc yêu cầu các nền tảng tuỳ chỉnh bổ sung sẽ làm tăng nhu cầu về hiệu suất của thiết bị. Để đảm bảo ứng dụng hoạt động hiệu quả, hãy kiểm thử ứng dụng trên các thiết bị mà người dùng sẽ sử dụng.

Theo mặc định, ARCore sẽ yêu cầu 2 luồng:

  1. Luồng YUV CPU 1x, hiện luôn là 640x480.
    ARCore sử dụng luồng này để theo dõi chuyển động.
  2. Luồng GPU 1x, thường là 1920x1080
    Dùng Session#getCameraConfig() để xác định độ phân giải hiện tại của luồng GPU.

Bạn có thể thay đổi độ phân giải của luồng GPU trên các thiết bị được hỗ trợ bằng cách sử dụng getSupportedCameraConfigs()setCameraConfig().

Theo chỉ báo sơ bộ, bạn có thể dự kiến:

Loại thiết bị Số lượng thiết bị phát trực tuyến đồng thời được hỗ trợ
Điện thoại cao cấp
  • 2 luồng CPU YUV, ví dụ: 640x4801920x1080
  • 1 luồng GPU, ví dụ: 1920x1080
  • 1 ảnh tĩnh độ phân giải cao (JPEG) đôi khi, ví dụ: 12MP
Điện thoại tầm trung
  • 2 luồng CPU YUV, ví dụ: 640x4801920x1080
  • 1 luồng GPU, ví dụ: 1920x1080
–hoặc–
  • 1 luồng CPU YUV, ví dụ: 640x480 –hoặc– 1920x1080
  • 1 luồng GPU, ví dụ: 1920x1080
  • 1 ảnh tĩnh độ phân giải cao (JPEG) đôi khi, ví dụ: 12MP

Để sử dụng các nền tảng tuỳ chỉnh, chẳng hạn như nền tảng trình đọc hình ảnh CPU, hãy nhớ thêm nền tảng đó vào danh sách các nền tảng cần được cập nhật (ví dụ: ImageReader).

Java

sharedCamera.">setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));

Kotlin

sharedCamera.">setAppSurfaces(this.cameraId, listOf(imageReader.surface))

Mở camera

Mở camera bằng lệnh gọi lại được bao bọc bằng 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)

Sử dụng lệnh gọi lại trạng thái thiết bị camera

Trong lệnh gọi lại trạng thái thiết bị camera, hãy lưu trữ một thông tin tham chiếu đến thiết bị camera và bắt đầu một phiên chụp mới.

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()
}

Tạo một phiên chụp mới

Tạo yêu cầu chụp mới. Sử dụng TEMPLATE_RECORD để đảm bảo rằng yêu cầu chụp tương thích với ARCore và cho phép chuyển đổi liền mạch giữa chế độ không phải AR và chế độ AR trong thời gian chạy.

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)
  }
}

Bắt đầu ở chế độ không phải AR hoặc chế độ AR

Để bắt đầu chụp khung hình, hãy gọi captureSession.setRepeatingRequest() từ lệnh gọi lại trạng thái phiên chụp ảnh bằng camera onConfigured(). Tiếp tục phiên ARCore trong lệnh gọi lại onActive() để bắt đầu ở chế độ thực tế tăng cường.

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)
}

Chuyển đổi liền mạch giữa chế độ không phải thực tế tăng cường hoặc chế độ thực tế tăng cường trong thời gian chạy

Cách chuyển từ chế độ không phải thực tế tăng cường sang chế độ thực tế tăng cường và tiếp tục một phiên ARCore đang tạm dừng:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

Cách chuyển từ chế độ thực tế tăng cường sang chế độ không phải thực tế tăng cường:

Java

// Pause ARCore.
sharedSession.pause();

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

Kotlin

// Pause ARCore.
sharedSession.pause()

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