본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] Camera2 API 를 알아보자

by 돼지왕 왕돼지 2019. 1. 6.
반응형


https://developer.android.com/reference/android/hardware/camera2/package-summary.html

https://inducesmile.com/android/android-camera2-api-example-tutorial/

http://pierrchen.blogspot.kr/2015/01/android-camera2-api-explained.html


active capture session, android camera api deprecated, android camera2 api, camera id, camera 속성 정보, camera2 api level, camera2 api tutorial, camera2 os, camera2 pacakge, camera2 surface, CameraCaptureSession, CameraCharacteristics, CameraDevice, CameraManager, CaptureRequest, CaptureRequestSession, createCaptureSeeion, getCameraCharacteristics, ImageReader, marshmellow, MediaRecorder, mos, pipeline, Preview, request builder, setRepeatingRequest, StateCallback, Surface, surfacetexture, [android] Camera2 API 를 알아보자


-

API Level 21 ( Marshmellow ) 부터 사용 가능하다.

기존에 쓰이던 android.hardware.Camera 패키지의 camera 는 deprecated 되었다.



-

android.hardware.camera2 패키지의 카메라는 장비를 pipeline 으로 본다.

Single frame capture request 는 1개의 output 을 image buffer, metadata 와 함께 제공해준다.

Request 들은 순서대로 처리되며 여러개의 request 를 한번에 요청할 수도 있다.



-

CameraManager instance 를 통해 작업한다.

각각의 CameraDevice 는 CameraCharacteristics 라는 카메라 속성에 대한 Model 을 가지고 있고, CameraManager.getCameraCharacteristics(String cameraId) 을 통해 얻어 올 수 있다.



-

stream 혹은 image 를 가져오기 위해서는 먼저 void createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) 를 통해 camera capture session 이라는 것을 만들어야 한다. 이 session 은 surface 와 연결되어 있다.

이 때 각각의  surface 는 적당한 사이즈와 포맷으로 미리 설정되어 있어야 한다.



-

Preview 는 보통 SurfaceTexture, Still picture 는 ImageReader, 그리고 video 는 MediaRecorder 에 연결된다.



-

앱은 request builder 등을 통해 CaptureRequest 를 만들고, active capture session 에게 전달하면 image 를 얻어올 수 있다.



-

CaptureRequestSession 은 CaptureRequest 가 전달될 context 이다.

initialized 된 surface 를 제공함으로서 session 을 얻어올 수 있다.


capture api 는 still picture 를 얻을 때 사용하고,

setRepeatingRequest 는 preview 를 보여줄 때 사용한다.



-

@ manifest

minSdkVersion 이 21(Marshmellow) 이상이여야 한다. ( 그 이전버전을 지원하려면 기존의 camera package 를 사용해야 한다. )

아래의 permission 과 uses-feature 설정이 필요하다.

...
<uses-permission android:name="android.permission.CAMERA" />
...
<uses-feature android:name="android.hardware.camera2.full" />
...



-

@ Source

public class MainActivity extends Activity {

    private static final int REQUEST_CAMERA_PERMISSION = 1234;

    private TextureView mTextureView;

    private CameraDevice mCamera;
    private Size mPreviewSize;
    private CameraCaptureSession mCameraSession;
    private CaptureRequest.Builder mCaptureRequestBuilder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 카메라 권한 체크
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
            return;
        }

        initTextureView();
    }

    private void initTextureView() {
        mTextureView = (TextureView) findViewById(R.id.texture);
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
                Log.e("cklee", "MMM onSurfaceTextureAvailable");
                openCamera();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
                Log.e("cklee", "MMM onSurfaceTextureSizeChanged");
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
                Log.e("cklee", "MMM onSurfaceTextureDestroyed");
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
                // 화면 갱신시마다 불림
//                Log.e("cklee", "MMM onSurfaceTextureUpdated");
            }
        });
    }

    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String[] cameraIdArray = manager.getCameraIdList();
            Log.e("cklee", "MMM cameraIds = " + Arrays.deepToString(cameraIdArray));

            // test 로 0 번 camera 를 사용
            String oneCameraId = cameraIdArray[0];

            CameraCharacteristics cameraCharacter = manager.getCameraCharacteristics(oneCameraId);
            Log.e("cklee", "MMM camraCharacter = " + cameraCharacter);

            StreamConfigurationMap map = cameraCharacter.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizesForStream = map.getOutputSizes(SurfaceTexture.class);
            Log.e("cklee", "MMM sizesForStream = " + Arrays.deepToString(sizesForStream));

            // 가장 큰 사이즈부터 들어있다
            mPreviewSize = sizesForStream[0];

            manager.openCamera(oneCameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice cameraDevice) {
                    mCamera = cameraDevice;
                    showCameraPreview();
                }

                @Override
                public void onDisconnected(@NonNull CameraDevice cameraDevice) {
                    mCamera.close();
                }

                @Override
                public void onError(@NonNull CameraDevice cameraDevice, int errorCode) {
                    Log.e("cklee", "MMM errorCode = " + errorCode);
                    mCamera.close();
                    mCamera = null;
                }
            }, null);
        } catch (CameraAccessException e) {
            Log.e("cklee", "MMM openCamera ", e);
        }
    }

    private void showCameraPreview() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            Surface textureViewSurface = new Surface(texture);

            mCaptureRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mCaptureRequestBuilder.addTarget(textureViewSurface);
            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

            mCamera.createCaptureSession(Arrays.asList(textureViewSurface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                    mCameraSession = cameraCaptureSession;
                    updatePreview();
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                    Log.e("cklee", "MMM onConfigureFailed");
                }
            }, null);
        } catch (CameraAccessException e) {
            Log.e("cklee", "MMM showCameraPreview ", e);
        }
    }

    private void updatePreview() {
        try {
            mCameraSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, null);
        } catch (CameraAccessException e) {
            Log.e("cklee", "MMM updatePreview", e);
        }
    }

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                Toast.makeText(this, "Permission denied", Toast.LENGTH_LONG).show();
                finish();
            }
        } else {
            openCamera();
        }
    }
}


-

request 관련하여 마지막 param 은 대부분 handler 인데, background handler 를 사용하는 것이 좋다.



반응형

댓글