Akses kamera bersama dengan ARCore

Panduan developer ini memandu Anda melakukan langkah-langkah untuk memungkinkan aplikasi beralih dengan lancar antara kontrol eksklusif kamera melalui Android Camera2 API dan berbagi akses kamera dengan ARCore.

Topik ini mengasumsikan bahwa Anda:

Mem-build dan menjalankan aplikasi contoh

Saat Anda mem-build dan menjalankan aplikasi contoh Shared Camera Java, aplikasi ini akan membuat sesi ARCore yang mendukung akses kamera bersama. Aplikasi dimulai dalam mode non-AR, dengan ARCore dijeda.

Saat aplikasi beroperasi dalam mode non-AR, penampil kamera akan menampilkan efek warna sepia. Saat beralih ke mode AR, efek sepia akan dinonaktifkan saat aplikasi menampilkan kontrol kamera ke ARCore dengan melanjutkan sesi yang dijeda.

Anda dapat menggunakan tombol AR di aplikasi untuk mengubah mode. Selama pratinjau, kedua mode menampilkan jumlah frame berkelanjutan yang diambil oleh Camera2.

Untuk membangun dan menjalankan aplikasi contoh Java Kamera Bersama:

  1. Download dan ekstrak Google ARCore SDK untuk Android.

  2. Buka project samples/shared_camera_java.

  3. Pastikan perangkat Android Anda terhubung ke mesin pengembangan melalui USB. Lihat Perangkat yang didukung ARCore untuk informasi selengkapnya.

  4. Di Android Studio, klik Run .

  5. Pilih perangkat Anda sebagai target deployment, lalu klik OK untuk meluncurkan aplikasi contoh di perangkat.

  6. Di perangkat, konfirmasi bahwa Anda ingin mengizinkan aplikasi mengambil gambar dan merekam video.

  7. Jika diminta untuk melakukannya, update atau instal versi terbaru ARCore.

  8. Gunakan tombol AR untuk beralih antara mode non-AR dan AR.

Ringkasan cara memungkinkan aplikasi berbagi akses kamera dengan ARCore

Ikuti langkah-langkah berikut untuk menerapkan akses kamera bersama dengan ARCore di aplikasi Anda. Semua cuplikan kode tersedia di SharedCameraActivity.java dalam contoh shared_camera_java.

Minta izin CAMERA

Agar dapat menggunakan kamera perangkat, pengguna harus memberikan izin CAMERA ke aplikasi Anda. Contoh ARCore menyertakan CameraPermissionHelper, yang menyediakan utilitas untuk meminta izin yang benar untuk aplikasi Anda.

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

Pastikan ARCore diinstal dan merupakan versi terbaru

ARCore harus diinstal dan diupdate agar dapat digunakan. Cuplikan berikut menunjukkan cara meminta penginstalan ARCore jika belum diinstal di perangkat.

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

Membuat sesi ARCore yang mendukung berbagi kamera

Hal ini melibatkan pembuatan sesi serta penyimpanan referensi dan ID kamera bersama 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

(Opsional) Beri tahu ARCore tentang platform kustom apa pun

Meminta platform kustom tambahan akan meningkatkan permintaan performa perangkat. Untuk memastikan performanya baik, uji aplikasi Anda di perangkat yang akan digunakan pengguna.

ARCore akan meminta dua streaming secara default:

  1. 1x streaming CPU YUV, saat ini selalu 640x480.
    ARCore menggunakan aliran ini untuk pelacakan gerakan.
  2. Aliran GPU 1x, biasanya 1920x1080
    Gunakan Session#getCameraConfig() untuk menentukan resolusi aliran GPU saat ini.

Anda dapat mengubah resolusi aliran GPU pada perangkat yang didukung menggunakan getSupportedCameraConfigs() dan setCameraConfig().

Sebagai indikator kasar, Anda bisa mendapatkan:

Jenis perangkat Mendukung streaming serentak
Ponsel kelas atas
  • 2x aliran CPU YUV, misalnya 640x480 dan 1920x1080
  • 1x aliran GPU, misalnya 1920x1080
  • 1x gambar diam beresolusi tinggi sesekali (JPEG), mis. 12MP
Ponsel kelas menengah
  • 2x aliran CPU YUV, misalnya 640x480 dan 1920x1080
  • 1x aliran GPU, misalnya 1920x1080
–atau–
  • 1x aliran CPU YUV, misalnya 640x480 –atau– 1920x1080
  • 1x aliran GPU, misalnya 1920x1080
  • 1x gambar diam beresolusi tinggi sesekali (JPEG), mis. 12MP

Untuk menggunakan platform kustom, seperti platform pembaca image CPU, pastikan untuk menambahkannya ke daftar platform yang perlu diupdate (misalnya, ImageReader).

Java

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

Kotlin

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

Membuka kamera

Buka kamera menggunakan callback yang digabungkan dengan 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)

Menggunakan callback status perangkat kamera

Di callback status perangkat kamera, simpan referensi ke perangkat kamera, dan memulai sesi pengambilan gambar baru.

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

Membuat sesi pengambilan gambar baru

Buat permintaan pengambilan gambar baru. Gunakan TEMPLATE_RECORD untuk memastikan bahwa permintaan pengambilan gambar kompatibel dengan ARCore, dan untuk memungkinkan peralihan yang lancar antara mode non-AR dan AR saat runtime.

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

Mulai dalam mode non-AR atau AR

Untuk mulai mengambil frame, panggil captureSession.setRepeatingRequest() dari callback status onConfigured() sesi pengambilan gambar kamera. Lanjutkan sesi ARCore dalam callback onActive() untuk memulai dalam mode 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)
}

Beralih dengan lancar antara mode non-AR atau AR saat runtime

Untuk beralih dari mode non-AR ke AR dan melanjutkan sesi ARCore yang dijeda:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

Untuk beralih dari mode AR ke mode non-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()