본문 바로가기

멀티미디어/Camera

카메라를 이용하자! - SurfaceView에 대한 이해

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

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



카메라를 이용하는 것에 대해 알아보기 전에, 카메라를 이용하면 필수로 사용하게 되는 카메라 프리뷰(Preview)를 표시할 때 사용하는 View인 SurfaceView에 대해 먼저 알아보도록 하겠습니다.

SurfaceView? 그게 뭐야?

SufraceView라는 이름에서 알 수 있듯이, TextView, ImageView처럼 컨텐츠를 표시할 수 있는 View 중 하나입니다. 하지만 이 SurfaceView는 다른 View들과는 달리 직접 SurfaceView가 컨텐츠를 표시하지 않습니다. 이건 좀 나중에 알아보기로 하고, 왜 하필이면 SurfaceView를 쓰는 걸까요?

일반적인 View는 화면에 뷰를 표시하는 연산과 기타 연산, 사용자와의 상호작용 처리 등이 모두 하나의 쓰레드에서 처리됩니다. 이것을 가장 잘 확인할 수 있는 예가 바로 ANR(Application Not Responding)입니다. ANR은 어플리케이션이 5초 이상 동작을 멈췄을 때, 조금 더 자세히 말하자면 GUI업데이트가 5초이상 멈췄을 때 발생하는데, 실제로 가끔씩 몇몇 어플리케이션에서 연산이 늦어지면 GUI 업데이트가 늦어지며 ANR이 발생하는 것을 볼 수 있습니다. 즉, GUI 업데이트와 다른 연산이 같은 쓰레드 내에서 처리되기에 이런 현상이 발생하는것이죠.

그런데, 우리가 처리해야할 것은 카메라 프리뷰, 즉 실시간으로 화상을 카메라로부터 받아서 1초에 몇십프레임 이상의 속도로 화면을 업데이트해야 하는 동작입니다. 그렇기에 만약 SurfaceView를 쓰지 않으면 뷰를 업데이트하는데 쓰레드의 자원을 모두 써서 어플리케이션의 정상적인 동작을 보장하기 어렵죠.

그래서 등장한 것이 바로 SurfaceView입니다. SurfaceView는 화면 업데이트를 백그라운드 쓰레드로 수행하여 어플리케이션의 자원을 잠식하지 않고 원활하게 뷰를 업데이트해줍니다. 뿐만 아니라 SurfaceView는 OpenGL을 통한 가속이 지원되어 원활한 3D그래픽 표현도 가능합니다. (GLSurfaceView)


SurfaceView의 구조

ImageView나 TextView는 뷰 자체가 바로 컨텐츠를 표시하지만, SurfaceView는 조금 복잡한 구조를 가지고 있습니다.
SurfaceView 자체는 하나의 "틀" 역할만 하고, 실제로 컨텐츠가 표시되는 곳은 SurfaceView 내의 Surface 객체입니다. 


위의 그림에서 알 수 있듯이, SurfaceHolder객체가 실제 Surface에 접근하여 화면을 처리해주는 구조를 가지고 있습니다. SurfaceHolder라는 이름 그대로, Surface 객체를 잡고(Hold) 관리해주는 것이라 보면 됩니다.

그럼, 이제 본격적으로 코드를 보면서 진행해보도록 하겠습니다. 아래의 코드는 API Demos의 CameraPreview 코드입니다.
우선 SurfaceView 부분부터 보도록 하겠습니다.


class Preview extends SurfaceView implements SurfaceHolder.Callback {
    SurfaceHolder mHolder;
       
    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) {
       
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
       
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        
    }

}

SurfaceView를 사용하기 위해서는 SurfaceView 객체를 직접 사용하는 것이 아니라 SurfaceView를 상속하며, SurfaceHolder.Callback을 구현하는 클래스를 생성해야 합니다.  생성자 부분을 보도록 하죠.

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

우선 클래스의 멤버로 SurfaceHolder 객체인 mHolder 객체를 가지고 있는 것을 확인할 수 있습니다. 이 객체를 통해 실제로 컨텐츠가 표시되는 영역인 Surface를 관리할 수 있습니다. (1)

그 다음, mHolder에 getHolder()메소드를 통해 현재 SurfaceView의 SurfaceHolder 인스턴스를 연결해주고 있는 것을 확인할 수 있습니다. 이로서, mHolder객체를 통해 이 SurfaceView의 Surface에 접근할 수 있게 되었습니다. (2)

다음은 콜백(Callback)을 설정하는 모습입니다. 이 과정은 SurfaceHolder와 SurfaceView를 연결시켜주는 과정입니다. 이로써 Surface가 변경됨을 SurfaceHolder(mHolder)를 거쳐 최종적으로 SurfaceView에서도 알 수 있게 되었습니다. (3)

그 다음은 SurfaceView의 유형을 설정해주는데, 위의 옵션은 버퍼가 없이 화면을 표시할 때 사용합니다. 카메라 프리뷰는 별도의 버퍼가 없어도 되니 이 옵션을 사용합니다.

그 다음으로 SurfaceView.Callback에서 구현하는 메소드들을 확인할 수 있습니다.

  public void surfaceCreated(SurfaceHolder holder) {
       
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
       
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        
    }

}

위의 메소드들은 마치 액티비티의 생애주기와 비슷한 형태를 하고 있습니다. 실제로, SurfaceView 자체가 3D 그래픽 등 자원을 많이 사용하기에 사용하지 않을 때 (화면에서 보이지 않을 때) 적절한 처리를 하는 것이 매우 중요하므로 위의 메소드에서 그 작업을 처리해주게 됩니다.

그럼 여기까지 SufraceView에 대한 기본적인 것들에 대해 알아보았으니 다음 글에서는 실제로 Camera 객체를 얻어와 Preview를 표시하는 것까지 알아보도록 하겠습니다. :)

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

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