본문 바로가기
안드로이드 자바

[Android][Java] Camera2 API를 사용하여 카메라 미리보기 생성하기

by teamnova 2024. 1. 10.
728x90

안녕하세요 오늘은 Camera2 API 를 사용해보겠습니다.

 

Android Camera2 란?

android.hardware.camera2 패키지는 안드로이드 개발자 문서에서 카메라 하드웨어를 관리하기 위한 인터페이스를 제공합니다. 이 패키지에는 카메라 장치를 제어하고 상호 작용하기 위한 클래스와 인터페이스가 포함되어 있습니다. 주요 구성 요소로는 카메라 서비스에 접근하는 CameraManager, 단일 카메라를 나타내는 CameraDevice, 카메라 설정을 위한 CaptureRequest, 캡처 요청을 처리하는 CameraCaptureSession, 이미지 데이터를 다루는 ImageReader 등이 있습니다. 이 API는 노출, 초점 등 카메라 매개변수에 대한 자세한 제어, 고해상도 이미지 캡처 지원, 전문가급 사진 앱에 적합한 복잡한 카메라 기능을 제공합니다.

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

 

android.hardware.camera2  |  Android Developers

android.inputmethodservice

developer.android.com

주요 특징

  1. 고급 제어 기능: Camera2 API는 ISO, 초점, 노출 시간, 프레임 속도 등과 같은 카메라 설정에 대한 세밀한 제어를 가능하게 합니다.
  2. 다양한 카메라 지원: 전면, 후면 및 외부 카메라를 포함한 다양한 카메라 타입을 지원합니다.
  3. 렌즈 및 센서 제어: 렌즈의 광학적 속성, 센서의 특성 등을 제어할 수 있습니다.
  4. RAW 이미지 지원: 고해상도 RAW 이미지 캡처를 지원하여 사진 편집 시 더 많은 유연성을 제공합니다.
  5. 프레임 버퍼 액세스: 캡처된 각 프레임에 대한 저수준 액세스를 제공하여 이미지 처리와 분석을 용이하게 합니다.
  6. 비디오 촬영: 고해상도 비디오 녹화 및 프레임 단위 처리 기능을 제공합니다.

주요 컴포넌트

  1. CameraManager: 시스템 서비스로서, 사용 가능한 카메라 목록을 제공하고 특정 카메라를 열기 위해 사용됩니다.
  2. CameraDevice: 개별 카메라 장치를 나타내며, 캡처 세션을 시작하기 위해 사용됩니다.
  3. CameraCaptureSession: 카메라 캡처 요청을 관리하며, 사진 촬영이나 비디오 녹화에 필요한 요청을 카메라에 전송합니다.
  4. CaptureRequest: 캡처할 이미지의 특정 파라미터를 설정합니다. 예를 들어, ISO, 초점, 노출 등을 설정할 수 있습니다.
  5. ImageReader: 캡처된 이미지 데이터를 읽기 위한 인터페이스입니다.

manifests 에 아래의 권한을 넣어주세요

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

MainActivity.java 

public class MainActivity extends AppCompatActivity {

    // 카메라 미리보기를 위한 TextureView
    private TextureView textureView;

    // 카메라 디바이스를 참조하기 위한 객체
    private CameraDevice cameraDevice;

    // 캡처 요청을 만들기 위한 Builder
    private CaptureRequest.Builder captureRequestBuilder;

    // 카메라 캡처 세션
    private CameraCaptureSession cameraCaptureSession;

    // 카메라 사용 권한 요청 코드
    private static final int REQUEST_CAMERA_PERMISSION = 200;

    // TextureView의 상태를 감지하는 리스너
    private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            // 텍스쳐뷰가 사용 가능할 때 카메라를 여는 함수 호출
            openCamera();
        }

        // 텍스처 크기가 변경될 때 호출
        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        }

        // 텍스쳐가 파괴될 때 호출
        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }

        // 텍스쳐가 업데이트될 때 호출
        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        }
    };

    // 카메라 상태 콜백
    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            // 카메라가 성공적으로 열리면 CameraDevice 인스턴스를 할당하고 미리보기 시작
            cameraDevice = camera;
            createCameraPreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            // 카메라 연결이 끊기면 닫음
            cameraDevice.close();
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            // 에러 발생시 카메라 닫고 null 할당
            cameraDevice.close();
            cameraDevice = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textureView = findViewById(R.id.textureView);
        // TextureView에 리스너 설정
        textureView.setSurfaceTextureListener(textureListener);
        Button takePictureButton = findViewById(R.id.btn_takepicture);
        takePictureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 사진 촬영 버튼 클릭 이벤트
                // takePicture(); // 사진 촬영 메소드 호출
            }
        });
    }

    // 카메라 미리보기를 생성하는 메소드
    private void createCameraPreview() {
        try {
            SurfaceTexture texture = textureView.getSurfaceTexture();
            assert texture != null;
            // 텍스처의 크기를 카메라 미리보기 크기로 설정
            texture.setDefaultBufferSize(1920, 1080);
            Surface surface = new Surface(texture);
            // 미리보기를 위한 CaptureRequest.Builder 설정
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureRequestBuilder.addTarget(surface);
            // 카메라 캡처 세션 생성
            cameraDevice.createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    // 카메라가 이미 닫혔다면 아무 작업도 수행하지 않음
                    if (cameraDevice == null) {
                        return;
                    }
                    // 캡처 세션 시작
                    cameraCaptureSession = session;
                    updatePreview();
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {
                    // 구성 실패시 Toast 메시지 출력
                    Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
                }
            }, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    // 카메라를 여는 메소드
    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String cameraId = manager.getCameraIdList()[0];
            // 카메라 권한 체크
            if (ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{"android.permission.CAMERA"}, REQUEST_CAMERA_PERMISSION);
                return;
            }
            // 카메라 오픈, 상태 콜백과 핸들러 설정
            manager.openCamera(cameraId, stateCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    // 카메라 미리보기를 업데이트하는 메소드
    private void updatePreview() {
        if (cameraDevice == null) {
            Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
            return;
        }
        // 자동 모드로 미리보기 설정
        captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        try {
            // 미리보기 요청을 반복하여 캡처 세션에 제출
            cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onPause() {
        // 액티비티가 일시정지되면 카메라를 닫음
        super.onPause();
        if (cameraDevice != null) {
            cameraDevice.close();
            cameraDevice = null;
        }
    }
}

 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/btn_takepicture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Take Picture" />

</RelativeLayout>