สิทธิ์เข้าถึงกล้องที่แชร์กับ ARCore

คู่มือนักพัฒนาซอฟต์แวร์นี้จะอธิบายขั้นตอนการอนุญาตให้แอปสลับไปมาระหว่างการควบคุมกล้องโดยเฉพาะอย่างราบรื่นผ่าน Android Camera2 API และการแชร์การเข้าถึงกล้องด้วย ARCore

หัวข้อนี้จะถือว่าคุณ:

สร้างและเรียกใช้แอปตัวอย่าง

เมื่อคุณสร้างและเรียกใช้แอปตัวอย่างของ Shared Camera Java จะเป็นการสร้างเซสชัน ARCore ที่รองรับการเข้าถึงกล้องที่ใช้ร่วมกัน แอปจะเริ่มต้นในโหมดที่ไม่ใช่ AR และ ARCore หยุดชั่วคราว

เมื่อแอปทำงานในโหมดที่ไม่ใช่ AR โปรแกรมดูภาพของกล้องจะแสดงเอฟเฟกต์สีซีเปีย เมื่อเปลี่ยนเป็นโหมด AR เอฟเฟกต์ซีเปียจะปิดเมื่อแอปคืนการควบคุมกล้องกลับไปเป็น ARCore โดยกลับมาใช้งานเซสชันที่หยุดชั่วคราวต่อ

คุณใช้สวิตช์ AR ในแอปเพื่อเปลี่ยนโหมดได้ ระหว่างการแสดงตัวอย่าง ทั้ง 2 โหมดจะแสดงจำนวนเฟรมที่ต่อเนื่องซึ่งกล้อง Camera2 จับภาพไว้ได้

หากต้องการสร้างและเรียกใช้แอปตัวอย่าง Java สำหรับกล้องที่แชร์ ให้ทำดังนี้

  1. ดาวน์โหลดและแตกไฟล์ Google ARCore SDK สำหรับ 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.java ภายในตัวอย่าง shared_camera_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 หากยังไม่ได้ติดตั้งในอุปกรณ์

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

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 จะขอสตรีม 2 รายการโดยค่าเริ่มต้น

  1. สตรีม CPU YUV 1 รายการ ปัจจุบันใช้ 640x480 เสมอ
    ARCore ใช้สตรีมนี้สำหรับการติดตามการเคลื่อนไหว
  2. สตรีม GPU 1 เท่า โดยทั่วไปจะอยู่ที่ 1920x1080
    ใช้ Session#getCameraConfig() เพื่อระบุความละเอียดของสตรีม GPU ปัจจุบัน

คุณเปลี่ยนความละเอียดของสตรีม GPU ได้ในอุปกรณ์ที่รองรับโดยใช้ getSupportedCameraConfigs() และ setCameraConfig()

ดังตัวอย่างต่อไปนี้

ประเภทอุปกรณ์ รองรับการสตรีมพร้อมกัน
โทรศัพท์ระดับไฮเอนด์
  • สตรีม CPU YUV 2 รายการ เช่น 640x480 และ 1920x1080
  • สตรีม GPU 1 เท่า เช่น 1920x1080
  • ภาพนิ่งความละเอียดสูงเป็นครั้งคราว 1 เท่า (JPEG) เช่น 12MP
โทรศัพท์ระดับกลาง
  • สตรีม CPU YUV 2 รายการ เช่น 640x480 และ 1920x1080
  • สตรีม GPU 1 เท่า เช่น 1920x1080
–หรือ–
  • สตรีม CPU YUV 1 รายการ เช่น 640x480 – หรือ 1920x1080
  • สตรีม GPU 1 เท่า เช่น 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

หากต้องการเริ่มจับภาพเฟรม ให้เรียกใช้ captureSession.setRepeatingRequest() จากเซสชันการจับภาพของกล้อง onConfigured() โค้ดเรียกกลับ ดำเนินเซสชัน ARCore ต่อภายในโค้ดเรียกกลับของ onActive() เพื่อเริ่มในโหมด 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()