Accès à la caméra partagé avec ARCore

Ce guide du développeur explique comment permettre à votre application de basculer facilement entre le contrôle exclusif de la caméra via l'API Android Camera2 et le partage de l'accès à la caméra avec ARCore.

Dans cette rubrique, nous partons du principe que vous avez:

Créer et exécuter l'application exemple

Lorsque vous compilez et exécutez l'application exemple Shared Camera Java (Appareil photo partagé), celui-ci crée une session ARCore compatible avec l'accès à la caméra partagée. L'application démarre en mode sans RA et ARCore est suspendu.

Lorsque l'application fonctionne en mode non-RA, la visionneuse de l'appareil photo affiche un effet de couleur sépia. Lorsque vous passez en mode RA, l'effet sépia se désactive lorsque l'application rend le contrôle de l'appareil photo à ARCore en reprenant la session suspendue.

Vous pouvez utiliser le bouton de RA dans l'application pour changer de mode. En preview, les deux modes affichent le nombre d'images continues capturées par Camera2.

Pour compiler et exécuter l'exemple d'application Java de la caméra partagée:

  1. Téléchargez et extrayez le SDK Google ARCore pour Android.

  2. Ouvrez le projet samples/shared_camera_java.

  3. Assurez-vous que votre appareil Android est connecté à l'ordinateur de développement via USB. Pour en savoir plus, consultez la section Appareils compatibles avec ARCore.

  4. Dans Android Studio, cliquez sur Run .

  5. Choisissez votre appareil comme cible de déploiement, puis cliquez sur OK pour lancer l'application exemple sur votre appareil.

  6. Sur l'appareil, confirmez que vous souhaitez autoriser l'application à prendre des photos et à enregistrer des vidéos.

  7. Si vous y êtes invité, mettez à jour ou installez la dernière version d'ARCore.

  8. Utilisez le bouton bascule AR pour passer du mode RA au mode standard et inversement.

Présentation de l'activation du partage de l'accès à l'appareil photo avec ARCore pour une application

Suivez ces étapes pour implémenter l'accès à la caméra partagée avec ARCore dans votre application. Tous les extraits de code sont disponibles dans SharedCameraActivity.java dans l'exemple shared_camera_java.

Demander l'autorisation CAMERA

Pour pouvoir utiliser l'appareil photo de l'appareil, l'utilisateur doit accorder l'autorisation CAMERA à votre application. Les exemples ARCore incluent un CameraPermissionHelper, qui fournit des utilitaires permettant de demander l'autorisation appropriée pour votre application.

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

Assurez-vous qu'ARCore est installé et à jour

ARCore doit être installé et à jour pour pouvoir être utilisé. L'extrait de code suivant montre comment demander une installation d'ARCore si ce n'est pas déjà fait sur l'appareil.

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

Créer une session ARCore compatible avec le partage de la caméra

Cette opération implique la création de la session, et le stockage de la référence et de l'ID de la caméra partagée 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

(Facultatif) Informer ARCore de toutes les surfaces personnalisées

Le fait de demander des surfaces personnalisées supplémentaires augmente les exigences de performances de l'appareil. Pour vous assurer qu'elle fonctionne correctement, testez-la sur les appareils que vos utilisateurs utiliseront.

ARCore demandera deux flux par défaut:

  1. 1x flux de processeur YUV, actuellement toujours 640x480.
    ARCore utilise ce flux pour le suivi du mouvement.
  2. Flux de GPU 1x, généralement 1920x1080
    Utilisez Session#getCameraConfig() pour déterminer la résolution actuelle du flux GPU.

Vous pouvez modifier la résolution du flux de GPU sur les appareils compatibles à l'aide de getSupportedCameraConfigs() et setCameraConfig().

À titre indicatif, vous pouvez vous attendre à ce qui suit:

Type d'appareil Flux simultanés acceptés
Téléphones haut de gamme
  • Deux flux de processeur YUV YUV, par exemple 640x480 et 1920x1080.
  • 1 flux GPU, par exemple 1920x1080
  • 1 image fixe haute résolution de temps à autre (JPEG), par exemple 12MP
Téléphones de milieu de gamme
  • Deux flux de processeur YUV YUV, par exemple 640x480 et 1920x1080.
  • 1 flux GPU, par exemple 1920x1080
ou
  • 1 flux de processeur YUV (YU), par exemple 640x480 –ou– 1920x1080
  • 1 flux GPU, par exemple 1920x1080
  • 1 image fixe haute résolution de temps à autre (JPEG), par exemple 12MP

Pour utiliser des surfaces personnalisées, telles qu'une surface de lecteur d'images de processeur, veillez à les ajouter à la liste des surfaces à mettre à jour (par exemple, une ImageReader).

Java

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

Kotlin

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

Ouvrir l'appareil photo

Ouvrez l'appareil photo à l'aide d'un rappel encapsulé 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)

Utiliser le rappel d'état de la caméra

Dans le rappel d'état de l'appareil photo, stockez une référence à cet appareil et démarrez une nouvelle session de capture.

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

Créer une session de capture

Créez une demande de capture. Utilisez TEMPLATE_RECORD pour vous assurer que la requête de capture est compatible avec ARCore et pour permettre le basculement fluide entre le mode non-RA et le mode RA lors de l'exécution.

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

Commencer en mode non-RA ou RA

Pour commencer à capturer des images, appelez captureSession.setRepeatingRequest() à partir du rappel d'état onConfigured() de la session de capture de la caméra. Reprenez la session ARCore dans le rappel onActive() pour démarrer en mode RA.

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

Basculez facilement entre les modes non RA et RA lors de l'exécution

Pour passer du mode RA au mode RA et reprendre une session ARCore interrompue:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

Pour passer du mode RA au mode non RA, procédez comme suit:

Java

// Pause ARCore.
sharedSession.pause();

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();

Kotlin

// Pause ARCore.
sharedSession.pause()

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()