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:
Sie haben den ARCore-Schnellstart abgeschlossen.
Sie kennen die Android Camera2 API (Android-spezifisches Camera2-Beispiel)
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:
Laden Sie das Google ARCore SDK for Android herunter und entpacken Sie es.
Öffnen Sie das Projekt
samples/shared_camera_java.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.
Klicken Sie in Android Studio auf Run
.
Wählen Sie Ihr Gerät als Bereitstellungsziel aus und klicken Sie auf OK, um die Beispiel-App auf Ihrem Gerät zu starten.
Bestätigen Sie auf dem Gerät, dass Sie der App erlauben möchten, Fotos aufzunehmen und Videos aufzuzeichnen.
Wenn Sie dazu aufgefordert werden, aktualisieren oder installieren Sie die aktuelle Version von ARCore.
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 YUV-CPU-Stream, derzeit immer
640x480.
ARCore verwendet diesen Stream für die Bewegungserkennung. - Ein 1x-GPU-Stream, in der Regel
1920x1080
Verwenden SieSession#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 |
|
| Smartphones im mittleren Preissegment |
|
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()
.