En esta guía para desarrolladores, se explican los pasos para permitir que tu app cambie sin problemas entre el control exclusivo de la cámara mediante la API de Camera2 de Android y el uso compartido del acceso a la cámara con ARCore.
En este tema, se supone que:
Haber completado la guía de inicio rápido de ARCore
Debes estar familiarizado con la API de Camera2 de Android (consulta la muestra específica de Android de Camera2 para obtener más información).
Cómo compilar y ejecutar la app de muestra
Cuando compilas y ejecutas la app de ejemplo Shared Camera Java, se crea una sesión de ARCore que admite el acceso compartido a la cámara. La app se inicia en modo no RA, con ARCore pausado.
Cuando la app funciona en un modo que no es de RA, el visor de la cámara muestra un efecto de color sepia. Cuando se cambia al modo de RA, el efecto sepia se desactiva cuando la app muestra el control de la cámara a ARCore reanudando la sesión de pausa.
Usa el interruptor de RA en la app para cambiar de modo. Durante la vista previa, ambos modos muestran la cantidad de fotogramas continuos capturados por Camera2.
Para compilar y ejecutar la app de ejemplo de Java de cámara compartida, haz lo siguiente:
Descarga y extrae el SDK de Google ARCore para Android.
Abre el proyecto
samples/shared_camera_java
.Asegúrate de que tu dispositivo Android esté conectado a la máquina de desarrollo por USB. Consulta Dispositivos compatibles con ARCore para obtener información detallada.
En Android Studio, haz clic en Run .
Elige tu dispositivo como destino de implementación y haz clic en OK para iniciar la app del ejemplo en tu dispositivo.
En el dispositivo, confirma que quieres permitir que la app tome fotos y grabe videos.
Si se te solicita, actualiza o instala la versión más reciente de ARCore.
Usa el interruptor AR para alternar entre los modos de RA y no.
Descripción general de cómo habilitar una app para compartir el acceso a la cámara con ARCore
Sigue estos pasos para implementar el acceso compartido a la cámara con ARCore en tu app. Todos los fragmentos de código están disponibles en SharedCameraActivity.java
dentro de la muestra shared_camera_java
.
Solicita permiso CAMERA
Para poder usar la cámara del dispositivo, el usuario debe otorgar a tu app el permiso CAMERA
.
Las muestras de ARCore incluyen un CameraPermissionHelper
, que proporciona utilidades para solicitar el permiso correcto para tu 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)
}
}
Asegúrate de que ARCore esté instalado y actualizado
ARCore debe estar instalado y actualizado para poder usarlo. En el siguiente fragmento, se muestra cómo solicitar la instalación de ARCore si aún no está instalado en el 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
}
}
}
Crea una sesión de ARCore que admita el uso compartido de la cámara
Esto implica crear la sesión y almacenar la referencia y el ID de la cámara compartida de 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
(Opcional) Informa a ARCore sobre cualquier superficie personalizada
La solicitud de plataformas personalizadas adicionales aumenta las demandas de rendimiento del dispositivo. Para asegurarte de que funcione bien, prueba tu app en los dispositivos que usarán los usuarios.
ARCore solicitará dos transmisiones de forma predeterminada:
- Transmisión de CPU YUV 1x, actualmente siempre
640x480
ARCore usa esta transmisión para el seguimiento de movimiento. - Una transmisión de GPU de 1x, por lo general,
1920x1080
UsaSession#getCameraConfig()
para determinar la resolución de transmisión de GPU actual.
Puedes cambiar la resolución de la transmisión de GPU en dispositivos compatibles mediante getSupportedCameraConfigs()
y setCameraConfig()
.
Como indicador aproximado, puedes esperar lo siguiente:
Tipo de dispositivo | Se admiten transmisiones simultáneas |
---|---|
Teléfonos de alta gama |
|
Teléfonos de gama media |
|
Para usar superficies personalizadas, como una plataforma de lector de imágenes de CPU, asegúrate de agregarla a la lista de superficies que se deben actualizar (por ejemplo, ImageReader
).
Java
sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));
Kotlin
sharedCamera.setAppSurfaces(this.cameraId, listOf(imageReader.surface))
Abre la cámara
Abre la cámara con una devolución de llamada unida de 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)
Cómo usar la devolución de llamada de estado del dispositivo de cámara
En la devolución de llamada de estado del dispositivo de cámara, almacena una referencia al dispositivo de cámara y comienza una nueva sesión de captura.
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()
}
Crear una nueva sesión de captura
Compila una solicitud de captura nueva. Usa TEMPLATE_RECORD
para asegurarte de que la solicitud de captura sea compatible con ARCore y permitir un cambio fluido entre los modos no RA y de RA durante el tiempo de ejecución.
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)
}
}
Cómo comenzar en modo no RA o RA
Para comenzar a capturar fotogramas, llama a captureSession.setRepeatingRequest()
desde la devolución de llamada de estado onConfigured()
de la sesión de captura de la cámara.
Reanuda la sesión de ARCore en la devolución de llamada onActive()
para comenzar en modo de RA.
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)
}
Cambia sin problemas entre modos que no son de RA y de RA durante el tiempo de ejecución
Para cambiar del modo no basado en RA al de RA y reanudar una sesión de ARCore pausada, haz lo siguiente:
Java
// Resume the ARCore session.
resumeARCore();
Kotlin
// Resume the ARCore session.
resumeARCore()
Para cambiar del modo de RA al modo no RA, haz lo siguiente:
Java
// Pause ARCore.
sharedSession.pause();
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();
Kotlin
// Pause ARCore.
sharedSession.pause()
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()