Accesso alla fotocamera condiviso con ARCore

Questa guida per gli sviluppatori illustra i passaggi per attivare il passaggio a un'app perfettamente tra il controllo esclusivo della videocamera, tramite API Android Camera2 e condividere l'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 fotocamera 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 colore seppia effetto. Quando passi alla modalità AR, l'effetto seppia viene disattivato nell'app restituisce il controllo della fotocamera ad ARCore ripristinando la sessione in pausa.

Per cambiare modalità, puoi utilizzare l'opzione AR nell'app. Durante l'anteprima, entrambe le modalità mostra il numero di fotogrammi continui acquisiti dalla fotocamera 2.

Per creare ed eseguire l'app di esempio Java della fotocamera condivisa:

  1. Scarica ed estrai SDK Google ARCore per Android.

  2. Apri l'app Progetto samples/shared_camera_java.

  3. Assicurati che il tuo dispositivo Android sia connesso al computer di sviluppo tramite USB. Vedi Dispositivi supportati di ARCore per informazioni dettagliate.

  4. In Android Studio, fai clic su Run .

  5. Scegli il tuo dispositivo come destinazione dell'implementazione e fai clic su OK per avviare di esempio sul tuo dispositivo.

  6. Sul dispositivo, conferma che vuoi consentire all'app di scattare foto e registrare un video.

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

  8. Utilizza l'opzione AR per passare dalla modalità non AR a quella AR e viceversa.

Panoramica dell'abilitazione di un'app per la condivisione dell'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 nel shared_camera_java campione.

Richiedi l'autorizzazione CAMERA

Per poter utilizzare la fotocamera del dispositivo, l'utente deve concedere alla tua app l'autorizzazione CAMERA. Gli esempi di ARCore includono CameraPermissionHelper, che fornisce le 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

ARCore deve essere installato e aggiornato prima di poter essere utilizzato. Il seguente snippet mostra come richiedere un'installazione di ARCore se non è già 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 fotocamera

Ciò comporta la creazione della sessione e l'archiviazione del riferimento e dell'ID di ARCore Videocamera condivisa:

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) Informa ARCore di eventuali piattaforme personalizzate

La richiesta di altre piattaforme personalizzate aumenta le esigenze di rendimento delle dispositivo. Per assicurarti che funzioni correttamente, testa l'app sui dispositivi su cui che gli utenti utilizzeranno.

Per impostazione predefinita, ARCore richiede due flussi:

  1. 1 stream con CPU YUV, attualmente sempre 640x480.
    ARCore utilizza questo stream per il monitoraggio del movimento.
  2. Uno stream GPU 1x, in genere 1920x1080
    Usa 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 indicativo, sono previsti i seguenti aspetti:

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

Per utilizzare piattaforme personalizzate, ad esempio un lettore di immagini CPU, assicurati di aggiungerlo all'elenco delle piattaforme che devono essere aggiornate (ad esempio, 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

Nel callback dello stato del dispositivo della videocamera, memorizza un riferimento al dispositivo della videocamera. 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 assicurare che la richiesta di acquisizione sia compatibile con ARCore e per consentire passando dalla modalità non AR a quella 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 frame, chiama captureSession.setRepeatingRequest() dal callback di stato della sessione di acquisizione della videocamera onConfigured(). Riprendi la sessione ARCore all'interno del callback onActive() per avviare la 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 problemi tra le modalità non AR e AR in fase di runtime

Per passare dalla modalità non AR ad AR e riprendere una sessione ARCore messa 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()