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

Ce guide du développeur vous explique comment faire passer votre application entre le contrôle exclusif de la caméra via API Android Camera2 et en partageant l'accès à l'appareil photo avec ARCore.

Dans cet article, 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, celle-ci crée un Session ARCore compatible avec l'accès à une caméra partagée. Le démarrage de l'application n'est pas effectué en RA et ARCore en pause.

Lorsque l'application fonctionne en mode non-RA, la visionneuse de l'appareil photo affiche une couleur sépia l'effet. Lorsque vous passez en mode RA, l'effet sépia se désactive renvoie le contrôle de l'appareil photo à ARCore en relançant la session suspendue.

Vous pouvez utiliser le bouton de RA dans l'application pour changer de mode. En preview, les deux modes affiche le nombre d'images prises en continu par Camera2.

Pour compiler et exécuter l'exemple d'application Java Shared Camera, procédez comme suit:

  1. Téléchargez et extrayez le fichier 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. Voir la page Appareils compatibles avec ARCore pour obtenir des informations détaillées.

  4. Dans Android Studio, cliquez sur Run .

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

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

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

  8. Utilisez le bouton bascule AR pour basculer entre les modes non-RA et RA.

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

Suivez ces étapes pour implémenter l'accès à l'appareil photo partagé avec ARCore dans votre application. Tous les extraits de code sont disponibles SharedCameraActivity.java dans les shared_camera_java échantillon.

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 élément CameraPermissionHelper, qui fournit des utilitaires pour demander l'autorisation correcte 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

Vous devez installer et mettre à jour ARCore pour pouvoir l'utiliser. L'extrait de code suivant montre comment demander l'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 caméra

Pour cela, vous devez créer la session, puis stocker la référence et l'ID ARCore. caméra partagée:

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) Indiquer à ARCore les surfaces personnalisées

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

ARCore demandera deux flux par défaut:

  1. Flux de processeur YUV 1x (actuellement 640x480).
    ARCore utilise ce flux pour le suivi du mouvement.
  2. Un flux GPU x1, généralement 1920x1080
    Utiliser 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 en utilisant getSupportedCameraConfigs() et setCameraConfig()

À titre d'indicateur approximatif, voici ce que vous pouvez attendre de cet indicateur:

Type d'appareil Flux simultanés acceptés
Téléphones haut de gamme
  • Deux flux de processeur YUV, par ex. 640x480 et 1920x1080
  • Flux GPU 1x, par ex. : 1920x1080
  • Image fixe haute résolution occasionnelle (1 fois) (JPEG), par exemple 12MP
Téléphones de milieu de gamme
  • Deux flux de processeur YUV, par ex. 640x480 et 1920x1080
  • Flux GPU 1x, par ex. : 1920x1080
–ou– <ph type="x-smartling-placeholder">
    </ph>
  • Flux de processeur YUV 1x, par exemple 640x480 – ou 1920x1080
  • Flux GPU 1x, par ex. : 1920x1080
  • Image fixe haute résolution occasionnelle (1 fois) (JPEG), par exemple 12MP

Pour utiliser des surfaces personnalisées telles qu'une surface de lecteur d'images pour 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))

Ouvrir l'appareil photo

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

Dans le rappel d'état de l'appareil photo, stockez une référence à l'appareil photo. 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. Utiliser TEMPLATE_RECORD pour garantir la compatibilité de la demande de capture avec ARCore et permettre entre le mode non-RA et le mode RA au moment 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 autre que RA ou RA

Pour commencer à capturer des images, 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)
}

Basculez facilement entre les modes non-RA et 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()