Android の Camera2 API を使って 写真を撮る

[Android の Camera2 API を使って カメラのプレビュー画面を表示する](https://qiita.com/ohwada/items/7bb65a566114066c8ced)
の続きです。

下記の公式サンプルを例に説明する。

[How to use basic functionalities of Camera2 API](https://github.com/googlesamples/android-Camera2Basic/tree/master/Application/src/main/java/com/example/android/camera2basic)

下記の記事を参考にした。

[Camera2 APIを使ったカメラ機能の実装](https://moewe-net.com/android/how-to-camera2)

### CameraDevice#createCaptureSession

カメラのキャプチャセッションを作成するメソッド。

[reference: CameraDevice#createCaptureSession](https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession(java.util.List%3Candroid.view.Surface%3E,%20android.hardware.camera2.CameraCaptureSession.StateCallback,%20android.os.Handler))

プレビュー画面を表示する場合は、下記のようにキャプチャする出力先に TexttureView の Surface を指定する。


```java
// TexttureView のSurface
    SurfaceTexture texture = mTextureView.getSurfaceTexture();
    texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
    Surface surface = new Surface(texture);

// キャプチャセッションを作成する
    List outputs = Arrays.asList(surface);
    cameraDevice.createCaptureSession(outputs, CameraCaptureSession.StateCallback, backgroundHandler);
            );
```
### ImageReader

カメラからの画像をキャプチャする Surfaceを持つクラス

[reference: ImageReader](https://developer.android.com/reference/android/media/ImageReader)

下記のようにインスタンスを生成する。

```java
// インスタンスを生成する
    ImageReader imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2);
// リスナーを設定する
    imageReader.setOnImageAvailableListener(
                        imageAvailableListener, backgroundHandler);
```

写真を撮る場合は、キャプチャする出力先 に
ImageReaderSurface の Surface を追加する。

```java
    List outputs = Arrays.asList(surface, imageReader.getSurface());
    cameraDevice.createCaptureSession(outputs, CameraCaptureSession.StateCallback, backgroundHandler);
            );
```
### CameraCaptureSession#capture

カメラにキャプチャする画像を要求するメソッド

[reference: CameraCaptureSession#capture](https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html#capture(android.hardware.camera2.CaptureRequest,%20android.hardware.camera2.CameraCaptureSession.CaptureCallback,%20android.os.Handler))

Picture ボタンをクリックすると、このメソッドを実行する。

```java
    previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
    captureSession.capture(previewRequestBuilder.build(), captureCallback, backgroundHandler);
```

CaptureCallback の中で、ImageReader をターゲットにして、このメソッドを実行する。

```java
    CaptureRequest.Builder captureBuilder =
    cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    captureBuilder.addTarget(imageReader.getSurface());
    captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
    captureSession.capture(captureBuilder.build(), captureCallback, backgroundHandler);
```

### OnImageAvailableListener#onImageAvailable

ImageReaderに画像が設定されたときに呼ばれるメソッド

[reference: OnImageAvailableListener#onImageAvailable](https://developer.android.com/reference/android/media/ImageReader.OnImageAvailableListener.html#onImageAvailable(android.media.ImageReader))

このメソッドが呼ばれた時は、下記のように画像の処理を行う。

```java
    backgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
```

### ImageSaver

画像をファイルに保存するクラス
Camera2API ではなく、公式サンプルで作成したもの

下記のように、ファイルに保存する

```java
    Image image;
    File file;
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    FileOutputStream output = new FileOutputStream(file);
    output.write(bytes);
```

### 画像の保存先

公式サンプルでは、アプリごとの領域である ExternalFilesDir に保存する。
この領域はアクセス権限が不要だが、保存された画像を見るためにはファイルマネージャーアプリが必要となる。

[reference: getExternalFilesDir](https://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String))

標準的なカメラアプリと同様に、SD カードの DCIM デレクトリの保存することもできる。
この領域は、SD カードのへのアクセス権限が必要となるが、
コンテンツプロバイダーに画像情報を登録して写真閲覧アプリで見ることができる。

前回 作成した Camera2Source は、基本的な処理は公式サンプルと同じであるが、下記の違いがある。
公式サンプルでは、onImageAvailable が呼ばれた時は ImageSaverを使って画像を保存する。
Camera2Sourceでは、onImageAvailableが呼ばれた時は コールバックで Activity に処理を戻し ImageSaver を使わずに画像を保存する。

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/Camera27

![camera27_saved.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/6836b9ed-1bad-97b2-89e6-7fd9de0cbe04.png)