Akses kamera bersama dengan ARCore

Panduan developer ini memandu Anda melalui langkah-langkah untuk mengaktifkan peralihan aplikasi tanpa hambatan di antara kontrol eksklusif kamera melalui API Camera2 Android dan berbagi akses kamera dengan ARCore.

Topik ini mengasumsikan bahwa Anda:

Mem-build dan menjalankan aplikasi contoh

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

Saat aplikasi beroperasi dalam mode non-AR, penampil kamera akan menampilkan warna sepia pengaruh tersebut. Saat beralih ke mode AR, efek sepia akan nonaktif saat aplikasi mengembalikan 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 membuat 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 ARCore Perangkat yang didukung untuk informasi lebih lanjut.

  4. Di Android Studio, klik Run .

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

  6. Pada perangkat, konfirmasi bahwa Anda ingin mengizinkan aplikasi ini untuk 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 pengaktifan aplikasi untuk 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 shared_camera_java contoh.

Meminta izin CAMERA

Agar dapat menggunakan kamera perangkat, pengguna harus memberi aplikasi Anda izin CAMERA. Contoh ARCore mencakup 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 sudah diinstal dan sudah diupdate

ARCore harus diinstal dan diupdate sebelum 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 dan penyimpanan referensi dan ID ARCore kamera bersama:

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) Memberi tahu ARCore tentang platform kustom apa pun

Meminta platform kustom tambahan akan meningkatkan tuntutan performa perangkat seluler. Untuk memastikannya berperforma baik, uji aplikasi Anda pada perangkat yang digunakan pengguna.

ARCore akan meminta dua streaming secara default:

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

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

Sebagai indikator kasar, Anda dapat memperkirakan:

Jenis perangkat Mendukung streaming secara bersamaan
Ponsel kelas atas
  • 2x streaming CPU YUV, misalnya 640x480 dan 1920x1080
  • 1x aliran GPU, misalnya 1920x1080
  • 1x sesekali gambar diam beresolusi tinggi (JPEG), mis. 12MP
Ponsel kelas menengah
  • 2x streaming 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 sesekali gambar diam beresolusi tinggi (JPEG), mis. 12MP

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

Java

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

Kotlin

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

Buka kamera

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

Dalam callback status perangkat kamera, simpan referensi ke perangkat kamera, dan memulai sesi pengambilan gambar yang 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()
}

Buat sesi pengambilan gambar baru

Buat permintaan perekaman baru. Gunakan TEMPLATE_RECORD untuk memastikan permintaan pengambilan gambar kompatibel dengan ARCore, dan untuk memungkinkan beralih 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)
  }
}

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