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

Hướng dẫn cho nhà phát triển này sẽ chỉ cho bạn các bước để cho phép ứng dụng chuyển đổi liền mạch giữa quyền kiểm soát độc quyền máy ảnh thông qua API Android Camera2 và chia sẻ quyền truy cập vào máy ảnh 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 tạo và chạy ứng dụng mẫu Shared Camera Java, bạn sẽ tạo một phiên ARCore hỗ trợ quyền truy cập vào máy ảnh dùng chung. Ứng dụng khởi động ở chế độ không phải AR, trong khi ARCore tạm dừng.

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

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

Cách tạo và chạy ứng dụng mẫu Java cho Máy ảnh dùng chung:

  1. Tải xuống và giải nén Google ARCore SDK dành 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 qua USB. Xem bài viết Các 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ị của bạn.

  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 thấy lời nhắc cập nhật, 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 thực tế tăng cường và chế độ thực tế tăng cường.

Tổng quan về cách cho phép ứng dụng chia sẻ quyền truy cập vào máy ảnh với ARCore

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

Yêu cầu quyền CAMERA

Để có thể dùng máy ảnh của thiết bị, người dùng phải cấp quyền CAMERA cho ứng dụng của bạn. Các mẫu ARCore bao gồm một CameraPermissionHelper, cung cấp các tiện ích để yêu cầu cấp đúng quyền 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 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 sử dụng được. Đoạn mã sau đây cho biết cách yêu cầu cài đặt ARCore nếu ARCore 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ợ chia sẻ camera

Quá trình này liên quan đến việc tạo phiên và lưu trữ tệp tham chiếu cũng như mã nhận dạng của máy ảnh 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 nền tảng tuỳ chỉnh

Việc yêu cầu thêm nền tảng tuỳ chỉnh sẽ làm tăng nhu cầu về hiệu suất của thiết bị. Để đảm bảo hoạt động của ứng dụng hoạt động tốt, 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 hai luồng:

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

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

Như một chỉ báo sơ bộ, bạn có thể kỳ vọng:

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

Để sử dụng các nền tảng tuỳ chỉnh, chẳng hạn như giao diện trình đọc hình ảnh CPU, hãy nhớ thêm giao diện đó vào danh sách nền tảng cần 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ở máy ảnh

Mở máy ảnh bằng lệnh gọi lại được gói 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ị máy ảnh

Trong lệnh gọi lại trạng thái thiết bị máy ảnh, hệ thống sẽ lưu trữ thông tin tham chiếu đến thiết bị máy ảnh và bắt đầu 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 một 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 thực tế tăng cường và chế độ thực tế tăng cường 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 thực tế tăng cường hoặc thực tế tăng cường

Để 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 onConfigured() của phiên chụp ảnh. Tiếp tục phiên ARCore trong lệnh gọi lại onActive() để bắt đầu ở chế độ 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)
}

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

Cách chuyển từ chế độ không thực tế tăng cường sang chế độ thực tế tăng cường (AR) và tiếp tục một phiên hoạt động ARCore đã 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 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()