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

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

Cette rubrique suppose que vous :

Créer et exécuter l'application exemple

Lorsque vous créez et exécutez l'application exemple Shared Camera Java, il crée une session ARCore qui prend en charge l'accès partagé à la caméra. L'application démarre en mode non AR, avec ARCore mis en veille.

Lorsque l'application fonctionne en mode non-RA, le visualiseur de caméras affiche un effet sépia. Lorsque vous passez en mode RA, l'effet sépia se désactive, car l'application redonne le contrôle de la caméra à ARCore en reprenant la session suspendue.

Vous pouvez utiliser le bouton bascule AR dans l'application pour changer de mode. Pendant l'aperçu, les deux modes affichent le nombre d'images continues capturées par Camera2.

Pour compiler et exécuter l'application exemple Shared Camera Java :

  1. Téléchargez et extrayez le SDK ARCore Google 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 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 AR pour passer du mode non RA au mode RA.

Présentation de l'activation du partage de l'accès à la caméra avec ARCore par une application

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

Demander l'autorisation CAMERA

Pour pouvoir utiliser l'appareil photo de l'appareil, l'utilisateur doit accorder à votre application l'autorisation CAMERA. 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 suivant montre comment demander l'installation d'ARCore s'il n'a pas déjà été installé 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 caméras

Cela implique de créer la session et de stocker la référence et 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 des surfaces personnalisées

Demander des surfaces personnalisées supplémentaires augmente les exigences de performances de l'appareil. Pour vous assurer qu'elle fonctionne correctement, testez votre application sur les appareils que vos utilisateurs utiliseront.

ARCore demande deux flux par défaut :

  1. 1 flux YUV CPU, actuellement toujours 640x480.
    ARCore utilise ce flux pour le suivi du mouvement.
  2. Un flux 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 GPU sur les appareils compatibles à l'aide de getSupportedCameraConfigs() et setCameraConfig().

Voici quelques indications approximatives :

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

Pour utiliser des surfaces personnalisées, comme une surface de lecteur d'image de processeur, veillez à l'ajouter à la liste des surfaces à mettre à jour (par exemple, un ImageReader).

Java

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

Kotlin

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

Ouvrez l'appareil photo.

Ouvrez la caméra à l'aide d'un rappel 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 de l'état de l'appareil photo

Dans le rappel de l'état de l'appareil photo, stockez une référence à l'appareil photo 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 une transition 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)
  }
}

Démarrer en mode non RA ou RA

Pour commencer à capturer des frames, appelez captureSession.setRepeatingRequest() à partir du rappel d'état onConfigured() de la session de capture de l'appareil photo. 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)
}

Passer facilement du mode non RA au mode RA au moment de l'exécution

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

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

Pour passer du mode RA au mode non RA :

Java

// Pause ARCore.
sharedSession.pause();

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

Kotlin

// Pause ARCore.
sharedSession.pause()

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