Akses kamera bersama dengan ARCore

Panduan developer ini memandu Anda melalui langkah-langkah untuk mengaktifkan aplikasi Anda agar dapat 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 mengembalikan kontrol kamera ke ARCore dengan melanjutkan sesi yang dijeda.

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

Untuk mem-build dan menjalankan aplikasi contoh Shared Camera Java:

  1. Download dan ekstrak the Google ARCore SDK for 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 mengetahui informasi mendetail.

  4. Di Android Studio, klik Run .

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

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

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

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

Ringkasan tentang cara mengaktifkan 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 contoh shared_camera_java.

Meminta izin CAMERA

Agar dapat menggunakan kamera perangkat, pengguna harus memberikan izin CAMERA ke aplikasi Anda. 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)
  }
}

Memastikan ARCore terinstal dan terbaru

ARCore harus diinstal dan terbaru 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 serta 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) Memberi tahu ARCore tentang platform kustom

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

ARCore akan meminta dua streaming secara default:

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

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

Sebagai indikator kasar, Anda dapat mengharapkan:

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

Untuk menggunakan platform kustom, seperti platform pembaca gambar 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 di-wrap 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 mulai sesi pengambilan 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 baru

Buat permintaan pengambilan baru. Gunakan TEMPLATE_RECORD untuk memastikan bahwa permintaan pengambilan 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)
  }
}

Memulai dalam mode non-AR atau AR

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