본문 바로가기

멀티미디어/Camera

카메라를 이용하자! - 카메라 프리뷰 띄우기

강좌 작성환경
SDK Version : Android SDK 1.6, release 2
ADT Version : 0.9.5

추후 SDK업데이트로 인해 글의 내용과 최신 SDK 내용간 차이가 있을 수 있습니다.

지난 글에 이어서 이번 글에서는 카메라 프리뷰를 SurfaceView에 표시하는 방법에 대해 알아보도록 하겠습니다.
카메라 프리뷰 화면이 우리가 만든 SurfaceView에 표시되어야 하므로, SurfaceView를 완전하게 구현해 주어야 합니다.
아래는 우리가 만든 SurfaceView를 상속한 클래스, Preview 클래스의 전체 코드입니다.


class Preview extends SurfaceView implements SurfaceHolder.Callback {
    SurfaceHolder mHolder;
    Camera mCamera;
    
    Preview(Context context) {
        super(context);
        
        // SurfaceHolder.Callback을 설정함으로써 Surface가 생성/소멸되었음을
        // 알 수 있습니다.
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // Surface가 생성되었다면, 카메라의 인스턴스를 받아온 후 카메라의
        // Preview 를 표시할 위치를 설정합니다.
        mCamera = Camera.open();
        try {
           mCamera.setPreviewDisplay(holder);
        } catch (IOException exception) {
            mCamera.release();
            mCamera = null;
            // TODO: add more exception handling logic here
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // 다른 화면으로 돌아가면, Surface가 소멸됩니다. 따라서 카메라의 Preview도 
        // 중지해야 합니다. 카메라는 공유할 수 있는 자원이 아니기에, 사용하지 않을
        // 경우 -액티비티가 일시정지 상태가 된 경우 등 - 자원을 반환해야합니다.
        mCamera.stopPreview();
        mCamera = null;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // 표시할 영역의 크기를 알았으므로 해당 크기로 Preview를 시작합니다.
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewSize(w, h);
        mCamera.setParameters(parameters);
        mCamera.startPreview();
    }

}

이전 글에서는 아직 surfaceCreated, surfaceDestroyed, surfaceChanged 메소드가 구현되어있지 않았는데, 여기에서 이 메소드들까지 구현되어 있는 것을 확인할 수 있습니다. 하나하나씩 차근차근 살펴보도록 하겠습니다.

class Preview extends SurfaceView implements SurfaceHolder.Callback {
    SurfaceHolder mHolder;
    Camera mCamera; // Camera객체 추가

우선, SurfaceView에 카메라에서 받은 영상을 표시하기 위해 Camera객체가 추가된 것을 확인할 수 있습니다.

    public void surfaceCreated(SurfaceHolder holder) {
        // Surface가 생성되었다면, 카메라의 인스턴스를 받아온 후 카메라의
        // Preview 를 표시할 위치를 설정합니다.
        mCamera = Camera.open();
        try {
           mCamera.setPreviewDisplay(holder);
        } catch (IOException exception) {
            mCamera.release();
            mCamera = null;
            // TODO: add more exception handling logic here
        }
    }
그 다음, surfaceCreated 메소드의 구현부입니다.
마치 액티비티의 생애주기 메소드 중 하나인 onCreate(Bundle)의 형태와 유사합니다. 이 메소드를 구현함으로써 SurfaceView가 생성되었을 때, 즉 "화면에 표시" 될 때 해야 할 동작을 처리할 수 있습니다.

여기에서는 카메라 객체를 받아와 카메라로부터 영상을 받을 수 있도록 초기화해주는 동작을 수행하고 있습니다. (Camera.open()) 그 다음, setPreviewDisplay(holder)메소드를 통해 카메라로부터 받은 프리뷰 영상을 어디에 표시해줄지 지정하고 있습니다.

앞의 글에서 SurfaceView는 SurfaceView 자체가 내용을 표시하는 것이 아니라 내부의 SurfaceHolder를 통해 최종적으로는 Surface라는 객체 위에 그 내용을 표시한다고 하였는데, 위의 경우는 아래와 같이 카메라의 영상이 처리된다고 볼 수 있습니다.


그 다음을 한번 보도록 하죠. 예외처리 부분이군요.

 } catch (IOException exception) {
            mCamera.release();
            mCamera = null;
            // TODO: add more exception handling logic here
        }

오류가 발생해서 프리뷰 화면을 제데로 표시하지 못했다면, release() 메소드를 사용하여 카메라의 자원을 다시 반환하는 것을 확인할 수 있습니다. 카메라는 여러 곳에서 공유할 수 있는 자원이 아니기에 카메라 객체의 사용이 끝났다면 위와 같이 release() 메소드를 사용하여 자원을 반환해야 합니다. 그렇지 않으면 다른 문제가 발생할 수 있습니다. :(

public void surfaceDestroyed(SurfaceHolder holder) {
        // 다른 화면으로 돌아가면, Surface가 소멸됩니다. 따라서 카메라의 Preview도 
        // 중지해야 합니다. 카메라는 공유할 수 있는 자원이 아니기에, 사용하지 않을
        // 경우 -액티비티가 일시정지 상태가 된 경우 등 - 자원을 반환해야합니다.
        mCamera.stopPreview();
        mCamera = null;
    }

그 다음, surfaceDestroyed 메소드 구현부입니다. 이 메소드는 SurfaceView가 더이상 화면에 표시되지 않을 때 호출됩니다. 일반적인 View는 액티비티가 일시정지 상태가 된 경우에도 계속 화면에 그 내용을 표시하지만, SurfaceVIew의 경우 표시하는 내용이 다른 뷰에 비해 복잡하기에 굳이 액티비티가 비활성 상태일때 그 내용을 표시할 이유가 없겠죠. 

따라서, stopPreview() 메소드를 사용하여 카메라의 프리뷰 영상을 표시하는 것을 중단하고, 카메라 객체를 소멸시킵니다.

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // 표시할 영역의 크기를 알았으므로 해당 크기로 Preview를 시작합니다.
        Camera.Parameters parameters = mCamera.getParameters(); // (1)
        parameters.setPreviewSize(w, h); // (2)
        mCamera.setParameters(parameters); // (3)
        mCamera.startPreview();
    }

다음은 surfaceChanged메소드입니다. 이 메소드는 surfaceCreated()메소드 호출 이후에 호출되는 메소드로, Surface의 크기에 따라 실질적으로 어떻게 내용을 표시할지를 처리해주는 메소드입니다.

여기에서는 카메라의 여러 설정값들을 담고 있는 parameters를 받아온 후(1), 프리뷰의 크기를 지정합니다.(2) 그 후, setParameters()에 방금 카메라 프리뷰 크기를 수정한 parameter 객체를 인자로 넘겨줌으로써 카메라 객체에서 프리뷰 영상을 표시할 영역의 크기를 설정해주게 됩니다. (3)

이 과정이 모두 끝났다면, startPreview() 메소드를 통해 최종적으로 카메라 프리뷰 영상을 표시해주게 됩니다.

여기까지 해서 SurfaceView 부분의 구현이 모두 끝났습니다. 실질적으로 카메라의 영상을 처리하는 부분은 여기에서 끝났으므로, 남은 것들은 액티비티를 구성하고, 이 뷰의 객체를 생성한 후 액티비티의 화면으로 표시해주는 과정이 남았습니다. 
액티비티 구현을 보도록 하죠.

public class CameraPreview extends Activity {    
    private Preview mPreview;
    
    @Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Hide the window title.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
    
        // Create our Preview view and set it as the content of our activity.
        mPreview = new Preview(this);
        setContentView(mPreview);
    }

}
다른 특이한 것들은 별로 없고, 액티비티의 이름을 표시해주는 타이틀바를 표시하지 않기 위해 requestWindowFeature(Window.FEATURE_NO_TITLE) 를 사용하는 것을 확인할 수 있습니다.

자 이제 거의 다 끝났습니다. 카메라를 사용하기 위해서는 권한이 필요하므로, 메니페스트 파일에 권한을 추가합니다.


위와 같이 메니페스트 파일의 Permission 탭으로 이동한 후, Add..버튼을 누릅니다.


Uses Permission을 선택한 후, OK 버튼을 클릭합니다.


새로 Uses Permission 항목에 카메라 사용 권한인 android.permission.CAMERA 를 선택해주면 권한 추가가 완료됩니다.

그리고, 메니페스트 파일을 열어 수동으로 <uses-feature android:name="android.hardware.camera"/> 를 추가합니다.

 
이 기능은 SDK버전을 선언하는 것과 비슷하게 카메라가 없는 장치에서는 아예 어플리케이션이 설치가 되지 않도록 하는 옵션입니다. 안드로이드를 사용하는 장치가 한두가지가 아니므로 이런 식으로 각 장치의 특성에 맞도록 적절히 조치를 해 주는 것이죠.

마지막으로, 액티비티를 landscape 모드로 표시하기 위해 메니페스트 파일을 열어 Application 탭으로 간후,


Screen orientation을 landscape로 바꾸어줍니다. 이렇게 하면 이 액티비티는 항상 landscape 모드로 표시되게 됩니다.


이 과정이 모두 끝났다면, 어플리케이션을 실행시켜봅시다. 카메라 미리보기가 잘 표시되는 것을 확인할 수 있습니다 :)



소스파일 첨부합니다.


'멀티미디어 > Camera' 카테고리의 다른 글

카메라를 이용하자! - SurfaceView에 대한 이해  (6) 2009.12.25