Accesso alla fotocamera condiviso con ARCore

Questa guida per gli sviluppatori illustra i passaggi per consentire alla tua app di passare senza interruzioni tra il controllo esclusivo della fotocamera tramite l'API Android Camera2 e la condivisione dell'accesso alla fotocamera con ARCore.

Questo argomento presuppone che tu:

Crea ed esegui l'app di esempio

Quando crei ed esegui l'app di esempio Shared Camera Java, viene creata una sessione ARCore che supporta l'accesso alla videocamera condivisa. L'app si avvia in modalità non AR, con ARCore in pausa.

Quando l'app funziona in modalità non AR, il visualizzatore della fotocamera mostra un effetto di colore seppia. Quando passi alla modalità AR, l'effetto seppia viene disattivato quando l'app restituisce il controllo della fotocamera ad ARCore ripristinando la sessione in pausa.

Per cambiare modalità puoi utilizzare l'opzione AR presente nell'app. Durante l'anteprima, in entrambe le modalità viene visualizzato il numero di fotogrammi continui acquisiti dalla Fotocamera2.

Per creare ed eseguire l'app Java di esempio per la videocamera condivisa:

  1. Scarica ed estrai l'SDK Google ARCore per Android.

  2. Apri il progetto samples/shared_camera_java.

  3. Assicurati che il tuo dispositivo Android sia collegato alla macchina di sviluppo tramite USB. Vedi ARCore Dispositivi supportati per informazioni dettagliate.

  4. In Android Studio, fai clic su Run .

  5. Scegli il tuo dispositivo come destinazione del deployment e fai clic su OK per avviare l'app di esempio sul tuo dispositivo.

  6. Conferma sul dispositivo che vuoi consentire all'app di scattare foto e registrare video.

  7. Se richiesto, aggiorna o installa l'ultima versione di ARCore.

  8. Utilizza l'interruttore AR per passare dalla modalità non AR alla modalità AR e viceversa.

Panoramica dell'attivazione di un'app per condividere l'accesso alla fotocamera con ARCore

Segui questi passaggi per implementare l'accesso alla fotocamera condivisa con ARCore nella tua app. Tutti gli snippet di codice sono disponibili in SharedCameraActivity.java all'interno dell'esempio shared_camera_java.

Richiedi l'autorizzazione CAMERA

Per poter utilizzare la fotocamera del dispositivo, l'utente deve concedere alla tua app l'autorizzazione CAMERA. Gli esempi ARCore includono un elemento CameraPermissionHelper, che fornisce utilità per richiedere l'autorizzazione corretta per la tua app.

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

Assicurati che ARCore sia installato e aggiornato

Per poter utilizzare ARCore, è necessario installare e aggiornare l'app. Lo snippet seguente mostra come richiedere l'installazione di ARCore se non è già stato installato sul dispositivo.

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

Creare una sessione ARCore che supporti la condivisione della videocamera

Ciò comporta la creazione della sessione e l'archiviazione del riferimento e dell'ID della videocamera condivisa 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

(Facoltativo) Comunicare ad ARCore eventuali piattaforme personalizzate

La richiesta di altre piattaforme personalizzate aumenta le esigenze di prestazioni del dispositivo. Per assicurarti che funzioni correttamente, testa l'app sui dispositivi che verranno utilizzati dagli utenti.

ARCore richiederà due flussi per impostazione predefinita:

  1. Stream della CPU YUV 1x, attualmente sempre sempre 640x480.
    ARCore utilizza questo stream per il monitoraggio del movimento.
  2. Uno stream GPU 1x, in genere 1920x1080
    Utilizza Session#getCameraConfig() per determinare l'attuale risoluzione dello stream GPU.

Puoi modificare la risoluzione dello stream GPU sui dispositivi supportati utilizzando getSupportedCameraConfigs() e setCameraConfig().

Come indicatore approssimativo, puoi aspettarti:

Tipo di dispositivo Stream simultanei supportati
Telefoni di fascia alta
  • Stream della CPU 2x YUV, ad esempio 640x480 e 1920x1080
  • 1x stream GPU, ad esempio 1920x1080
  • 1 immagine statica occasionale ad alta risoluzione (JPEG), ad esempio 12MP
Telefoni di fascia media
  • Stream della CPU 2x YUV, ad esempio 640x480 e 1920x1080
  • 1x stream GPU, ad esempio 1920x1080
–oppure–
  • 1x stream della CPU YUV, ad esempio 640x480 –o– 1920x1080
  • 1x stream GPU, ad esempio 1920x1080
  • 1 immagine statica occasionale ad alta risoluzione (JPEG), ad esempio 12MP

Per utilizzare piattaforme personalizzate, ad esempio una piattaforma per il lettore di immagini della CPU, assicurati di aggiungerla all'elenco delle piattaforme che devono essere aggiornate (ad esempio, una ImageReader).

Java

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

Kotlin

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

Apri la fotocamera

Apri la fotocamera utilizzando un callback con wrapping 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)

Usa il callback dello stato del dispositivo della videocamera

Nella funzionalità di callback dello stato del dispositivo della videocamera, archivia un riferimento al dispositivo della videocamera e avvia una nuova sessione di acquisizione.

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

Crea una nuova sessione di acquisizione

Crea una nuova richiesta di acquisizione. Utilizza TEMPLATE_RECORD per assicurarti che la richiesta di acquisizione sia compatibile con ARCore e per consentire il passaggio senza problemi dalla modalità non AR alla modalità AR in fase di 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)
  }
}

Avvia in modalità non AR o AR

Per iniziare ad acquisire i fotogrammi, chiama captureSession.setRepeatingRequest() dal callback dello stato della sessione di acquisizione della fotocamera onConfigured(). Riprendi la sessione ARCore all'interno del callback onActive() per iniziare in modalità 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)
}

Passa senza interruzioni tra le modalità non AR e AR in fase di runtime

Per passare dalla modalità non AR a quella AR e riprendere una sessione ARCore in pausa:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

Per passare dalla modalità AR a quella 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()