このデベロッパー ガイドでは、Android Camera2 API を介したカメラの排他制御と ARCore とのカメラ アクセスの共有をアプリがシームレスに切り替えられるようにする手順について説明します。
このトピックは、次のことを前提としています。
ARCore クイックスタートを完了している
Android Camera2 API に精通している(詳しくは、Android 固有の Camera2 サンプルをご覧ください)
サンプルアプリをビルドして実行する
Shared Camera Java サンプルアプリをビルドして実行すると、共有カメラ アクセスをサポートする ARCore セッションが作成されます。アプリは非 AR モードで起動し、ARCore は一時停止します。
アプリが非 AR モードで動作している場合、カメラ ビューアにはセピア色の効果が表示されます。AR モードに切り替えると、一時停止したセッションを再開してアプリがカメラ制御を ARCore に戻すため、セピア効果はオフになります。
アプリの AR スイッチを使用して、モードを切り替えることができます。プレビュー中、どちらのモードでも Camera2 でキャプチャされた連続フレームの数が表示されます。
Shared Camera Java サンプルアプリをビルドして実行するには:
Google ARCore SDK for Android をダウンロードして解凍します。
samples/shared_camera_javaプロジェクトを開きます。Android デバイスが USB 経由で開発マシンに接続されていることを確認します。詳しくは、ARCore のサポート対象デバイスをご覧ください。
Android Studio で、Run
をクリックします。
デプロイ ターゲットとしてデバイスを選択し、OK をクリックしてデバイスでサンプルアプリを起動します。
デバイスで、アプリが写真の撮影と動画の録画を行うことを許可することを確認します。
プロンプトが表示されたら、ARCore の最新バージョンを更新またはインストールします。
AR スイッチを使用して、非 AR モードと AR モードを切り替えます。
アプリが ARCore とカメラへのアクセスを共有できるようにする概要
アプリで ARCore を使用してカメラの共有アクセスを実装する手順は次のとおりです。すべてのコード スニペットは、shared_camera_java サンプル内の SharedCameraActivity.java で確認できます。
CAMERA 権限をリクエストする
デバイスのカメラを使用できるようにするには、ユーザーがアプリに CAMERA 権限を付与する必要があります。ARCore サンプルには、アプリの正しい権限をリクエストするユーティリティを提供する CameraPermissionHelper が含まれています。
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 がインストールされ、最新の状態であることを確認する
ARCore を使用するには、事前にインストールして最新の状態にしておく必要があります。次のスニペットは、ARCore がデバイスにまだインストールされていない場合に、ARCore のインストールをリクエストする方法を示しています。
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 セッションを作成する
これには、セッションの作成と、ARCore 共有カメラの参照と ID の保存が含まれます。
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
(省略可)カスタム サーフェスを ARCore に通知する
カスタム サーフェスを追加でリクエストすると、デバイスのパフォーマンス要件が増加します。アプリが適切に動作するように、ユーザーが使用するデバイスでアプリをテストしてください。
ARCore はデフォルトで 2 つのストリームをリクエストします。
- 1 つの YUV CPU ストリーム。現在は常に
640x480。
ARCore はこのストリームをモーション トラッキングに使用します。 - 1x GPU ストリーム(通常は
1920x1080
)Session#getCameraConfig()を使用して、現在の GPU ストリームの解像度を特定します。
サポートされているデバイスでは、getSupportedCameraConfigs() と setCameraConfig() を使用して GPU ストリームの解像度を変更できます。
おおよその目安として、次のことが想定されます。
| デバイスの種類 | サポートされている同時ストリーム数 |
|---|---|
| ハイエンド スマートフォン |
|
| ミッドレンジのスマートフォン |
|
CPU 画像リーダー サーフェスなどのカスタム サーフェスを使用するには、更新が必要なサーフェスのリスト(ImageReader など)に追加してください。
Java
sharedCamera.">setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));
Kotlin
sharedCamera.">setAppSurfaces(this.cameraId, listOf(imageReader.surface))
カメラを起動する
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)
カメラ デバイスの状態コールバックを使用する
カメラ デバイスの状態コールバックで、カメラ デバイスへの参照を保存し、新しいキャプチャ セッションを開始します。
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()
}
新しいキャプチャ セッションを作成する
新しいキャプチャ リクエストをビルドします。TEMPLATE_RECORD を使用して、キャプチャ リクエストが ARCore と互換性があることを確認し、実行時に非 AR モードと AR モードをシームレスに切り替えられるようにします。
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)
}
}
非 AR モードまたは AR モードで開始する
フレームのキャプチャを開始するには、カメラ キャプチャ セッションの onConfigured() 状態コールバックから captureSession.setRepeatingRequest() を呼び出します。onActive() コールバック内で ARCore セッションを再開して、AR モードで開始します。
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)
}
実行時に非 AR モードと AR モードをシームレスに切り替え
非 AR モードから AR モードに切り替えて、一時停止した ARCore セッションを再開するには:
Java
// Resume the ARCore session.
resumeARCore();
Kotlin
// Resume the ARCore session.
resumeARCore()
AR モードから非 AR モードに切り替えるには:
Java
// Pause ARCore.
sharedSession.pause();
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();
Kotlin
// Pause ARCore.
sharedSession.pause()
// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()
をクリックします。