ケルベロスさんのプログラミング / けるぷろ

プログラマのけるさんことケルベロスです

Androidでカメラアプリを作成する(Android5.0以降に対応) (2)

Androidでカメラアプリを作成する(Android5.0以降に対応) (2)

撮影機能を実装する

カメラを準備するメソッドの修正

.. 省略

private Handler mBackgroundHandler; // ->(1)
private ImageReader mImageReader;

.. 省略

private void prepareCameraView() {
    CameraManager cameraManager 
        = (CameraManager) mParentActivity.getSystemService(Context.CAMERA_SERVICE);
    try {
        String backCameraId = null;
        for (String cameraId : cameraManager.getCameraIdList()) {
            CameraCharacteristics characteristics 
                = cameraManager.getCameraCharacteristics(cameraId);
            if (characteristics.get(CameraCharacteristics.LENS_FACING)
                    == CameraCharacteristics.LENS_FACING_BACK) {
                backCameraId = cameraId;

                StreamConfigurationMap map
                    = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];

                Size jpegSize = getJPEGSize(characteristics); // ->(2)

                mImageReader = ImageReader.newInstance(jpegSize.getWidth(), // ->(3)
                        jpegSize.getHeight(),
                        ImageFormat.JPEG, 1);
                mImageReader.setOnImageAvailableListener(mReaderListener, mBackgroundHandler); // ->(4)
            }
        }

.. 省略

(1) バックグラウンドハンドラをメンバ変数として定義する onResumeでスレッドを開始させておく(コードは省略)
(2) ストリーム設定からJPEGの出力サイズを取得する
(3) 画像を取得するためのImageReaderの初期化
(4) Image取得リスナーをセットする 実装内容は後述

ImageReaderとは
SurfaceTextureの画像にアクセスする機能を提供するクラス

撮影メソッドの実装

.. 省略

    private void takePicture() {
        if(null == mCameraDevice) {
            return;
        }
        try {
            mCaptureRequestBuilder.addTarget(mImageReader.getSurface());            // ->(5)
            mCaptureRequestBuilder.set(                                             // ->(6)
                CaptureRequest.CONTROL_MODE, 
                CameraMetadata.CONTROL_MODE_AUTO);

            int rotation
                = mParentActivity.getWindowManager().getDefaultDisplay().getRotation(); // ->(7)
            mCaptureRequestBuilder.set(
                CaptureRequest.JPEG_ORIENTATION,
                ORIENTATIONS.get(rotation));

            mCaptureSession.capture(mCaptureRequestBuilder.build(),                // ->(8)
                    new CameraCaptureSession.CaptureCallback() {
                        @Override
                        public void onCaptureCompleted(@NonNull CameraCaptureSession session,  // ->(9)
                                                       @NonNull CaptureRequest request,
                                                       @NonNull TotalCaptureResult result) {
                            super.onCaptureCompleted(session, request, result);
                        }
                    },
                    mBackgroundHandler);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

.. 省略

(5) 生成したImageReaderからSurfaceを取得する
(6) 自動モードをセットする
(7) 画像の縦横を設定する
(8) キャプチャを開始する
(9) キャプチャ成功時のコールバック

ImageReaderのコールバック

キャプチャが成功するとImageReadr.OnImageAvailableListenerに結果がかえってくる

.. 省略

private ImageReader.OnImageAvailableListener mReaderListener
        = new ImageReader.OnImageAvailableListener() {

    public void onImageAvailable(ImageReader imageReader) {               // ->(10)

        Image image = imageReader.acquireNextImage();                     // ->(11)
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        OutputStream output = null;
        String saveDirectory = Environment.getExternalStorageDirectory().toString();
        String saveFileName = "pic_" + System.currentTimeMillis() + ".jpg";
        try {
            File file = new File(saveDirectory,saveFileName);
            output = new FileOutputStream(file);
            output.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (image != null) {
                image.close();
            }
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        String[] paths = {saveDirectory + "/" + saveFileName};         // ->(12)
        String[] mimeTypes = {"image/jpeg"};
        MediaScannerConnection.scanFile(mParentActivity.getApplicationContext(),
                paths,
                mimeTypes,
                new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(String s, Uri uri) {
                        mParentActivity.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(mParentActivity,"saved",Toast.LENGTH_LONG).show();
                                createCameraCaptureSession();         // ->(13)
                            }
                        });
                    }
                }
        );
    }

};

.. 省略

(10) 画像を取得可能になったときのコールバック
(11) バッファから画像をファイルとして保存していく処理
(12) MediaScannerConnectionでファイルをスキャンする これはメディアライブラリで使用可能にするための処理
(13) 再度プレビューを開始する

サンプルコード

githubに置いてありますので、参考にしていただければと思います。