สิทธิ์เข้าถึงกล้องที่แชร์กับ 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 1x ปัจจุบันเป็น 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
–หรือ–
  • สตรีม YUV CPU 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()