Kamerazugriff mit ARCore geteilt

In diesem Entwicklerleitfaden erfährst du, wie du mit deiner App nahtlos zwischen der exklusiven Steuerung der Kamera über die Android Camera2 API und der Freigabe des Kamerazugriffs mit ARCore hin- und herwechseln kannst.

In diesem Thema wird Folgendes vorausgesetzt:

Beispielanwendung erstellen und ausführen

Wenn Sie die Beispiel-App Shared Camera Java erstellen und ausführen, wird eine ARCore-Sitzung erstellt, die den gemeinsamen Kamerazugriff unterstützt. Die App wird im Nicht-AR-Modus gestartet, ARCore ist pausiert.

Wenn die App im Nicht-AR-Modus ausgeführt wird, zeigt die Kamera einen Sepia-Farbeffekt an. Wenn Sie in den AR-Modus wechseln, schaltet sich der Sepia-Effekt ab, da die pausierte Sitzung fortgesetzt wird, sobald die Kamerasteuerung auf ARCore zurückgesetzt wird.

Mit dem AR-Schalter in der App können Sie den Modus wechseln. In der Vorschau wird in beiden Modi die Anzahl der zusammenhängenden Frames angezeigt, die von Camera2 aufgenommen wurden.

So erstellen Sie die Java-Beispiel-App für freigegebene Kamera und führen sie aus:

  1. Laden Sie das Google ARCore SDK for Android herunter und extrahieren Sie es.

  2. Öffnen Sie das Projekt samples/shared_camera_java.

  3. Prüfen Sie, ob Ihr Android-Gerät über USB mit dem Entwicklungscomputer verbunden ist. Weitere Informationen finden Sie unter Unterstützte Geräte für ARCore.

  4. Klicke in Android Studio auf Run .

  5. Wählen Sie Ihr Gerät als Bereitstellungsziel aus und klicken Sie auf OK, um die Beispiel-App auf Ihrem Gerät zu starten.

  6. Bestätigen Sie auf dem Gerät, dass die App Fotos und Videos aufnehmen darf.

  7. Wenn du dazu aufgefordert wirst, aktualisiere oder installiere die neueste Version von ARCore.

  8. Mit dem Schalter AR können Sie zwischen dem Nicht-AR- und dem AR-Modus wechseln.

Freigabe des Kamerazugriffs durch Apps mit ARCore

Führe die folgenden Schritte aus, um den gemeinsamen Kamerazugriff mit ARCore in deiner App zu implementieren. Alle Code-Snippets sind in der SharedCameraActivity.java im Beispiel shared_camera_java verfügbar.

Berechtigung „CAMERA“ anfordern

Um die Kamera des Geräts verwenden zu können, muss der Nutzer deiner App die Berechtigung CAMERA erteilen. Die ARCore-Beispiele enthalten ein CameraPermissionHelper, das Dienstprogramme zum Anfordern der richtigen Berechtigung für Ihre App bereitstellt.

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

ARCore muss installiert und auf dem neuesten Stand sein

ARCore muss installiert und auf dem neuesten Stand sein, bevor es verwendet werden kann. Das folgende Snippet zeigt, wie eine Installation von ARCore angefordert wird, falls sie noch nicht auf dem Gerät installiert wurde.

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

ARCore-Sitzung erstellen, die die Kamerafreigabe unterstützt

Dazu wird die Sitzung erstellt und die Referenz und die ID der gemeinsam genutzten ARCore-Kamera gespeichert:

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

Optional: ARCore über benutzerdefinierte Oberflächen informieren

Wenn zusätzliche benutzerdefinierte Oberflächen angefordert werden, erhöht sich die Leistung des Geräts. Testen Sie Ihre App auf den Geräten, die Ihre Nutzer verwenden werden, um sicherzustellen, dass sie gut funktioniert.

ARCore fordert standardmäßig zwei Streams an:

  1. 1x YUV-CPU-Stream, aktuell immer 640x480.
    ARCore verwendet diesen Stream für die Bewegungserkennung.
  2. Ein 1x-GPU-Stream, in der Regel 1920x1080.
    Verwenden Sie Session#getCameraConfig(), um die aktuelle GPU-Stream-Auflösung zu ermitteln.

Sie können die Auflösung des GPU-Streams auf unterstützten Geräten mit getSupportedCameraConfigs() und setCameraConfig() ändern.

Als groben Indikator können Sie Folgendes erwarten:

Gerätetyp Unterstützung gleichzeitiger Streams
High-End-Smartphones
  • 2x YUV-CPU-Streams, z.B. 640x480 und 1920x1080
  • 1x GPU-Stream, z.B. 1920x1080
  • 1x gelegentliches Standbild mit hoher Auflösung (JPEG), z.B. 12MP
Smartphones der mittleren Preisklasse
  • 2x YUV-CPU-Streams, z.B. 640x480 und 1920x1080
  • 1x GPU-Stream, z.B. 1920x1080
–oder–
  • 1x YUV-CPU-Streams, z.B. 640x480 – oder 1920x1080
  • 1x GPU-Stream, z.B. 1920x1080
  • 1x gelegentliches Standbild mit hoher Auflösung (JPEG), z.B. 12MP

Wenn Sie benutzerdefinierte Oberflächen verwenden möchten, z. B. eine CPU-Bildleseoberfläche, müssen Sie sie der Liste der zu aktualisierenden Oberflächen hinzufügen (z. B. ImageReader).

Java

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

Kotlin

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

Kamera öffnen

Öffnen Sie die Kamera mithilfe eines mit ARCore verpackten Callbacks:

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)

Gerätestatus-Callback der Kamera verwenden

Im Callback zum Kameragerätestatus wird ein Verweis auf das Kameragerät gespeichert und eine neue Aufnahmesitzung gestartet.

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

Neue Aufnahmesitzung erstellen

Erstellen Sie eine neue Erfassungsanfrage. Verwenden Sie TEMPLATE_RECORD, damit die Aufnahmeanfrage mit ARCore kompatibel ist und während der Laufzeit ein nahtloser Wechsel zwischen dem Nicht-AR- und dem AR-Modus möglich ist.

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

Im Nicht-AR- oder AR-Modus starten

Um mit der Aufnahme von Bildern zu beginnen, rufen Sie captureSession.setRepeatingRequest() über den Callback onConfigured()-Status der Kamerasitzung auf. Setze die ARCore-Sitzung innerhalb des onActive()-Callbacks fort, um im AR-Modus zu starten.

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

Während der Laufzeit nahtlos zwischen AR- und AR-Modus wechseln

So wechseln Sie vom Nicht-AR-Modus in den AR-Modus und setzen eine pausierte ARCore-Sitzung fort:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

So wechseln Sie vom AR-Modus in den Nicht-AR-Modus:

Java

// Pause ARCore.
sharedSession.pause();

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

Kotlin

// Pause ARCore.
sharedSession.pause()

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