В этом руководстве разработчика рассказывается, как обеспечить беспрепятственное переключение вашего приложения между эксклюзивным управлением камерой через Android Camera2 API и общим доступом к камере с помощью ARCore.
Эта тема предполагает, что вы:
Вы завершили быстрый старт ARCore.
Знакомы с Android Camera2 API (более подробную информацию можно найти в примере Camera2 для Android ).
Создайте и запустите пример приложения.
Когда вы создаете и запускаете пример Java-приложения «Совместная камера» , он создает сеанс ARCore, который поддерживает доступ к общей камере. Приложение запускается в режиме без AR, а ARCore приостановлен.
Когда приложение работает в режиме без AR, средство просмотра камеры отображает цветовой эффект сепии. При переключении в режим AR эффект сепии отключается, поскольку приложение возвращает управление камерой ARCore, возобновляя приостановленный сеанс.
Вы можете использовать переключатель AR в приложении для изменения режимов. Во время предварительного просмотра в обоих режимах отображается количество непрерывных кадров, снятых камерой Camera2.
Чтобы создать и запустить пример Java-приложения «Совместная камера»:
Загрузите и извлеките Google ARCore SDK для Android .
Откройте проект
samples/shared_camera_java
.Убедитесь, что ваше устройство Android подключено к машине разработки через USB. Подробную информацию см. в разделе « Устройства, поддерживаемые ARCore».
В Android Studio нажмите Run .
.
Выберите свое устройство в качестве цели развертывания и нажмите OK , чтобы запустить пример приложения на своем устройстве.
На устройстве подтвердите, что вы хотите разрешить приложению делать снимки и записывать видео.
Если будет предложено это сделать, обновите или установите последнюю версию ARCore.
Используйте переключатель AR для переключения между режимами без AR и AR.
Обзор возможности предоставления приложению совместного доступа к камере с ARCore
Выполните следующие действия, чтобы реализовать общий доступ к камере с помощью ARCore в вашем приложении. Все фрагменты кода доступны в файле SharedCameraActivity.java
в shared_camera_java
.
Запросить разрешение CAMERA
Чтобы иметь возможность использовать камеру устройства, пользователь должен предоставить вашему приложению разрешение CAMERA
. Образцы ARCore включают CameraPermissionHelper
, который предоставляет утилиты для запроса правильного разрешения для вашего приложения.
Ява
protected void onResume() {
// Request the camera permission, if necessary.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this);
}
}
Котлин
override fun onResume() {
// Request the camera permission, if necessary.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this)
}
}
Убедитесь, что ARCore установлен и обновлен.
Прежде чем его можно будет использовать, ARCore должен быть установлен и обновлен. В следующем фрагменте показано, как запросить установку ARCore, если он еще не установлен на устройстве.
Ява
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;
}
}
Котлин
// 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, поддерживающий совместное использование камеры.
Это включает в себя создание сеанса и сохранение ссылки и идентификатора общей камеры ARCore:
Ява
// 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();
Котлин
// 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
(Необязательно) Сообщите ARCore о любых нестандартных поверхностях.
Запрос дополнительных пользовательских поверхностей увеличивает требования к производительности устройства. Чтобы убедиться, что оно работает хорошо, протестируйте свое приложение на устройствах, которые будут использовать ваши пользователи.
По умолчанию ARCore запросит два потока:
- 1 поток процессора YUV , в настоящее время всегда
640x480
.
ARCore использует этот поток для отслеживания движения . - 1 поток графического процессора , обычно
1920x1080
ИспользуйтеSession#getCameraConfig()
чтобы определить текущее разрешение потока графического процессора.
Вы можете изменить разрешение потока графического процессора на поддерживаемых устройствах, используя getSupportedCameraConfigs()
и setCameraConfig()
.
В качестве приблизительного показателя вы можете ожидать:
Тип устройства | Поддерживаются одновременные потоки |
---|---|
Высококлассные телефоны |
|
Телефоны среднего уровня |
|
Чтобы использовать пользовательские поверхности, такие как поверхность чтения изображений ЦП, обязательно добавьте ее в список поверхностей, которые необходимо обновить (например, ImageReader
).
Ява
sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));
Котлин
sharedCamera.setAppSurfaces(this.cameraId, listOf(imageReader.surface))
Откройте камеру
Откройте камеру с помощью обратного вызова, завернутого в ARCore:
Ява
// 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);
Котлин
// 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)
Используйте обратный вызов состояния устройства камеры
В обратном вызове состояния устройства камеры сохраняется ссылка на устройство камеры и начинается новый сеанс захвата.
Ява
public void onOpened(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " opened.");
SharedCameraActivity.this.cameraDevice = cameraDevice;
createCameraPreviewSession();
}
Котлин
fun onOpened(cameraDevice: CameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.id + " opened.")
this.cameraDevice = cameraDevice
createCameraPreviewSession()
}
Создать новый сеанс захвата
Создайте новый запрос на захват. Используйте TEMPLATE_RECORD
чтобы гарантировать совместимость запроса на захват с ARCore и обеспечить плавное переключение между режимами без AR и AR во время выполнения.
Ява
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);
}
}
Котлин
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)
}
}
Запуск в режиме без AR или AR.
Чтобы начать захват кадров, вызовите captureSession.setRepeatingRequest()
из обратного вызова состояния onConfigured()
сеанса захвата камеры. Возобновите сеанс ARCore в обратном вызове onActive()
, чтобы запустить его в режиме AR.
Ява
// 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);
}
Котлин
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)
}
Плавное переключение между режимами без AR и AR во время выполнения.
Чтобы переключиться из режима без AR в режим AR и возобновить приостановленный сеанс ARCore:
Ява
// Resume the ARCore session.
resumeARCore();
Котлин
// Resume the ARCore session.
resumeARCore()
Чтобы переключиться из режима AR в режим без AR:
Ява
// Pause ARCore.
sharedSession.pause();
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();
Котлин
// Pause ARCore.
sharedSession.pause()
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()