En esta guía para desarrolladores, se explican los pasos para habilitar tu app para que cambie sin problemas entre el control exclusivo de la cámara a través de la API de Android Camera2 y el uso compartido del acceso a la cámara con ARCore.
En este tema, se supone que ya hiciste lo siguiente:
Haber completado la guía de inicio rápido de ARCore
Conocimiento de la API de Android Camera2 (revisa la muestra de Camera2 específica para Android para obtener más información)
Cómo compilar y ejecutar la app de muestra
Cuando compilas y ejecutas la app de ejemplo de 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 en pausa.
Cuando la app funciona en modo no 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, ya que la app reanuda la sesión pausada y devuelve el control de la cámara a ARCore.
Puedes usar 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 Shared Camera en Java, 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 a través de un cable USB. Consulta la lista de 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 de 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 cambiar entre los modos de RA y no RA.
Descripción general para habilitar una app para que comparta 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 de shared_camera_java.
Solicita permiso de CAMERA
Para poder usar la cámara del dispositivo, el usuario debe otorgarle a tu app el permiso CAMERA.
Los ejemplos 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 usarse. En el siguiente fragmento, se muestra cómo solicitar la instalación de ARCore si aún no se instaló 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
Solicitar superficies personalizadas adicionales aumenta las exigencias de rendimiento del dispositivo. Para asegurarte de que funcione bien, prueba tu app en los dispositivos que usarán tus usuarios.
De forma predeterminada, ARCore solicitará dos transmisiones:
- 1x YUV CPU stream, actualmente siempre
640x480.
ARCore usa este flujo para el seguimiento de movimiento. - Un flujo de GPU de 1x, por lo general,
1920x1080
UsaSession#getCameraConfig()para determinar la resolución actual del flujo de GPU.
Puedes cambiar la resolución de la transmisión de GPU en dispositivos compatibles con 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 superficie de lector de imágenes de CPU, asegúrate de agregarla a la lista de superficies que deben actualizarse (por ejemplo, un 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 encapsulada en 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 del estado del dispositivo de la cámara
En el almacenamiento de devolución de llamada del 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()
}
Cómo crear una sesión de captura nueva
Crea una nueva solicitud de captura. Usa TEMPLATE_RECORD para garantizar que la solicitud de captura sea compatible con ARCore y permitir el cambio fluido entre el modo AR y el modo no AR en 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 iniciar en modo RA o no 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 dentro de la devolución de llamada onActive() para iniciar en el 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 los modos de RA y sin RA en el tiempo de ejecución
Para cambiar del modo no RA al modo RA y reanudar una sesión de ARCore en pausa, haz lo siguiente:
Java
// Resume the ARCore session.
resumeARCore();
Kotlin
// Resume the ARCore session.
resumeARCore()
Para cambiar del modo RA al modo sin 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()
.