Camera2 APIを使ったカメラ機能の実装|Android開発

      2018/11/14

Androidの新しいカメラモジュール、Camera2 API(android.hardware.camera2)
を使ったカメラ機能の実装方法です。
本記事を見ながら30分程度で撮影開始まで行えるよう、細かな調整は省略しています。

実装時の処理フローを追加しました。

permissionの追加

カメラを使用するためにはAndroidManifest.xmlで

<uses-permission android:name="android.permission.CAMERA"></uses-permission>

を指定しておく必要があります。

layout

プレビュー表示を行うためのTextureViewを用意します。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:tools="http://schemas.android.com/tools"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <TextureView
        android:id="@+id/camera2_texture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="visible" />

</FrameLayout>

カメラの利用手順

準備

  • TextureViewがavailableかどうかをチェックします。
mTextureView.isAvailable();
    • available(true)ならオープン処理を行います。
    • 非available(false)ならTextureView.SurfaceTextureListenerを設定して、availableになるのを待ちます。
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);

TextureView.SurfaceTextureListener mSurfaceTextureListener =
    new TextureView.SurfaceTextureListener() {...};
  • TextureView.SurfaceTextureListener.onSurfaceTextureAvailable()
    TextureViewがavailableになったらカメラのオープン処理を行います。

カメラのオープン

  • CameraManagerのインスタンスを取得します。
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
  • オープンするカメラのIDを決めます。
  • ImageReaderを生成します。ImageReaderは主に撮影時に使用します。
ImageReader mImageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 3);
mImageReader.setOnImageAvailableListener(mTakePictureAvailableListener, null);

mTakePictureAvailableListenerについては後述。

  • CameraManagerにオープン要求を出します。
cameraManager.openCamera(cameraId, mStateCallback, null);

CameraDevice.StateCallback mStateCallback =
    new CameraDevice.StateCallback() {...};
  • CameraDevice.StateCallback.onOpened()
    • SurfaceTextureにプレビューサイズを設定し、プレビュー用のSurfaceを生成します。
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(1280, 720);
Surface mPreviewSurface = new Surface(texture);
    • CaptureSessionを生成します。(cameraはonOpened()のパラメータ・CameraDevice)
camera.createCaptureSession(Arrays.asList(mPreviewSurface,
                                       mImageReader.getSurface()),
                           mSessionCallback,
                           null);

CameraCaptureSession.StateCallback mSessionCallback =
    new CameraCaptureSession.StateCallback() {...};
    • パラメータのCameraDeviceを保持しておきます。
mCameraDevice = camera;
  • CameraCaptureSession.StateCallback.onConfigured()
    • パラメータのCameraCaptureSessionを保持しておきます。
mCaptureSession = session;

準備からオープン完了までの処理フロー

水色背景は必要に応じて設定する箇所になります。

Camera2 open flow

撮影サイズと端末の向きからプレビューサイズを決定する方法
Camera2 撮影サイズから最適なプレビューサイズを決定する|Android開発

プレビューサイズからTextureViewのサイズを調整する方法
Camera2 TextureViewの回転(プレビューアスペクト比キープ)|Android開発

Camera2 open flow

プレビューの開始

  • プレビュー用のCaptureRequest.Builderを生成します。
CaptureRequest.Builder captureBuilder =
    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  • CaptureRequest.Builderにプレビュー用のSurfaceを設定します。
captureBuilder.addTarget(mPreviewSurface);
  • 必要なパラメータを設定します。(下記はサンプル)
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  • プレビューを開始します。
captureSession.setRepeatingRequest(captureBuilder.build(), null, null);

プレビュー開始のフロー

Camera2 preview flow

撮影

  • 撮影用のCaptureRequest.Builderを生成します。
CaptureRequest.Builder captureBuilder =
    camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
  • CaptureRequest.Builderに撮影用のSurfaceを設定します。
captureBuilder.addTarget(mImageReader.getSurface());
  • 必要なパラメータを設定します。(下記はサンプル)
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, orientation);
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
  • 現在のプレビューを停止します。
captureSession.stopRepeating();
  • 撮影を開始します。
captureSession.capture(captureBuilder.build(), mCaptureCallback, null);

CameraCaptureSession.CaptureCallback mCaptureCallback =
    new CameraCaptureSession.CaptureCallback() {...};
  • CameraCaptureSession.CaptureCallback.onCaptureCompleted()
    撮影完了のタイミングで通知されます。

撮影開始のフロー

Camera2 take picture flow

撮影画像の通知

カメラオープン時に指定したmTakePictureAvailableListenerが撮影画像を受信します。

ImageReader.OnImageAvailableListener mTakePictureAvailableListener =
    new ImageReader.OnImageAvailableListener() {...};
  • 撮影画像はImageReader.OnImageAvailableListener.onImageAvailable()で通知されます。
  • Image(android.media.Image)を取得します。
Image image = reader.acquireNextImage();

または

Image image = reader.acquireLatestImage();
  • 取得したImageを処理します。(保存など)
  • Imageを解放します。これを忘れるとバースト撮影などで失敗します。
image.close();

撮影画像通知のフロー

Camera2 take picture flow

撮影完了通知のフロー

Camera2 take picture finish flow

カメラのクローズ

  • CameraCaptureSessionをクローズします。
mCaptureSession.close();
  • CameraDeviceをクローズします。
mCameraDevice.close();
  • ImageReaderをクローズします。
mImageReader.close();

クローズのフロー

Camera2 close flow

 - Android