Kamerazugriff mit ARCore geteilt

In dieser Entwickleranleitung wird beschrieben, wie Sie Ihre App so einrichten, dass sie nahtlos zwischen der exklusiven Steuerung der Kamera über die Android Camera2 API und der gemeinsamen Nutzung des Kamerazugriffs mit ARCore wechseln kann.

In diesem Thema wird Folgendes vorausgesetzt:

Beispiel-App erstellen und ausführen

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

Wenn die App im Nicht-AR-Modus ausgeführt wird, wird im Kamerabild ein Sepia-Effekt angezeigt. Beim Wechsel in den AR-Modus wird der Sepia-Effekt deaktiviert, da die App die Kamerasteuerung an ARCore zurückgibt, indem die pausierte Sitzung fortgesetzt wird.

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

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

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

  2. Öffnen Sie das Projekt samples/shared_camera_java.

  3. Achten Sie darauf, dass Ihr Android-Gerät über USB mit der Entwicklungsmaschine verbunden ist. Weitere Informationen finden Sie unter Geräte mit ARCore-Unterstützung.

  4. Klicken Sie 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 Sie der App erlauben möchten, Fotos aufzunehmen und Videos aufzuzeichnen.

  7. Wenn Sie dazu aufgefordert werden, aktualisieren oder installieren Sie die aktuelle Version von ARCore.

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

Übersicht über die Freigabe des Kamerazugriffs einer App für ARCore

Folgen Sie dieser Anleitung, um den gemeinsamen Kamerazugriff mit ARCore in Ihrer App zu implementieren. Alle Code-Snippets sind im SharedCameraActivity.java-Beispiel im Ordner shared_camera_java verfügbar.

CAMERA-Berechtigung anfordern

Damit die Kamera des Geräts verwendet werden kann, muss der Nutzer Ihrer App die Berechtigung CAMERA erteilen. Die ARCore-Beispiele enthalten ein CameraPermissionHelper, das Hilfsprogramme zum Anfordern der richtigen Berechtigung für Ihre App bietet.

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 Sie eine Installation von ARCore anfordern, wenn es noch nicht auf dem Gerät installiert ist.

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 müssen Sie die Sitzung erstellen und die Referenz und ID der gemeinsam genutzten ARCore-Kamera speichern:

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 Sie zusätzliche benutzerdefinierte Oberflächen anfordern, steigen die Leistungsanforderungen des Geräts. Damit sie gut funktioniert, sollten Sie Ihre App auf den Geräten testen, die Ihre Nutzer verwenden.

ARCore fordert standardmäßig zwei Streams an:

  1. 1 YUV-CPU-Stream, derzeit 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 Auflösung des GPU-Streams zu ermitteln.

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

Als grobe Richtlinie gilt:

Gerätetyp Unterstützung von simultanen Streams
High-End-Smartphones
  • 2 YUV-CPU-Streams, z.B. 640x480 und 1920x1080
  • 1 GPU-Stream, z.B. 1920x1080
  • 1 x gelegentliches hochauflösendes Standbild (JPEG), z.B. 12MP
Smartphones im mittleren Preissegment
  • 2 YUV-CPU-Streams, z.B. 640x480 und 1920x1080
  • 1 GPU-Stream, z.B. 1920x1080
– oder –
  • 1 YUV-CPU-Stream, z.B. 640x480 oder 1920x1080
  • 1 GPU-Stream, z.B. 1920x1080
  • 1 x gelegentliches hochauflösendes Standbild (JPEG), z.B. 12MP

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

Java

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

Kotlin

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

Kamera öffnen

Kamera über einen ARCore-Wrapped-Callback öffnen:

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)

Callback für den Kameragerätestatus verwenden

Speichern Sie im Callback für den Kameragerätestatus eine Referenz auf das Kameragerät und starten Sie eine neue Aufnahmesitzung.

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 Aufzeichnungssitzung erstellen

Neue Erfassungsanfrage erstellen Verwenden Sie TEMPLATE_RECORD, um sicherzustellen, dass die Aufnahmeanfrage mit ARCore kompatibel ist, und um das nahtlose Umschalten zwischen Nicht-AR- und AR-Modus zur Laufzeit zu ermöglichen.

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

Rufen Sie captureSession.setRepeatingRequest() aus dem Status-Callback onConfigured() der Kameraaufnahmesitzung auf, um mit der Aufnahme von Frames zu beginnen. Setzen Sie die ARCore-Sitzung im onActive()-Callback 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)
}

Zur Laufzeit nahtlos zwischen Nicht-AR- und AR-Modi 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()