안드로이드 개발 SurfaceView에 카메라 영상 띄우는 방법

안드로이드 개발

안드로이드 개발 SurfaceView에 카메라 영상 띄우는 방법

 

환경: Android Studio

 

SurfaceView TextView, ImageView 와 같이 화면에 뭔가를 표현할 때 사용하는 안드로이드 위젯입니다. 모두 View 를 상속받아서 구현된 것은 맞지만 차이점이 있습니다. SurfaceView 는 컨텐츠를 표현할 때 View 내에 하나의 쓰레드에서 처리되지 않습니다. 고화질의 카메라 영상이나 애니메이션 화면을 구현해서 보여 줘야 하기 때문에 화면 업데이트를 백그라운드 스레드에서 처리한 후 전달 받은 데이터를 보여 주는 역할을 합니다. 그러니까 SurfaceView 는 단순히 영상을 보는 액자 역할만 하고 SurfaceHolder 클래스가 백그라운드에서 처리된 결과를 중간에서 주고 받아 관리합니다.

 

오늘은 SurfaceView SurfaceHolder 를 이용해서 스마트폰 카메라에서 받은 영상을 표현해 보겠습니다. 위에서 이야기 했듯이 SurfaceView 에 영상을 표현하기 위해서는 SurfaceHolder 가 필요합니다. 카메라 영상을 표현하기 위해서 SurfaceHolder.Callback implements 하고 필수 구현 함수 안에 SurfaceView 로 영상을 넘기기 위한 작업을 해야 합니다. 구현해야 되는 함수는 총 3가지 입니다. 

@Override
public void surfaceCreated(SurfaceHolder holder) {

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

surfaceCreated() SurfaceView 가 생성될 때 발생하는 함수로 카메라와 SurfaceHolder 와 연결하고 카메라 Preview를 시작합니다. surfaceChanged() 는 상태가 변경될 때 마다 발생하는 함수로 SurfaceView 에 맞게 카메라 Preview 도 재설정한 후 다시 시작하게 합니다. surfaceDestoryed() SurfaceView 객체가 사라지게 되면 발생하는 함수로 카메라 리소스를 반환합니다

@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        if (mCamera == null) {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        }
    } catch (IOException e) {
    }
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    // View 가 존재하지 않을 때
    if (mCameraHolder.getSurface() == null) {
        return;
    }

    // 작업을 위해 잠시 멈춘다
    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        // 에러가 나더라도 무시한다.
    }
    
    // 카메라 설정을 다시 한다. 
    Camera.Parameters parameters = mCamera.getParameters();
    List<String> focusModes = parameters.getSupportedFocusModes();
    if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }
    mCamera.setParameters(parameters);

    // View 를 재생성한다.
    try {
        mCamera.setPreviewDisplay(mCameraHolder);
        mCamera.startPreview();
    } catch (Exception e) {
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
}

다음은 카메라 리소스를 받고 SurfaceHolder SurfaceView 를 연결하는 부분입니다. 여기에서 mCamera.setDisplayOrientation(90) 을 한 것은 카메라를 가로에서 세로로 돌리기 위함입니다

private void init(){

    mCamera = Camera.open();
    mCamera.setDisplayOrientation(90);

    // surfaceview setting
    mCameraHolder = mCameraView.getHolder();
    mCameraHolder.addCallback(this);
    mCameraHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

메인 Activity를 구현한 xml 은 다음과 같습니다

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/cameraView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

</LinearLayout>

메인 Activity 전체 소스는 다음과 같습니다

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.camera2.CameraDevice;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Button;

import java.io.IOException;
import java.util.List;

public class CameraActivity extends Activity implements SurfaceHolder.Callback {

    private CameraDevice camera;
    private SurfaceView mCameraView;
    private SurfaceHolder mCameraHolder;
    private Camera mCamera;
    private Button mStart;
    private boolean recording = false;
    private MediaRecorder mediaRecorder;

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

        mCameraView = (SurfaceView)findViewById(R.id.cameraView);

        init();
    }

    private void init(){

        mCamera = Camera.open();
        mCamera.setDisplayOrientation(90);

        // surfaceview setting
        mCameraHolder = mCameraView.getHolder();
        mCameraHolder.addCallback(this);
        mCameraHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    // surfaceholder 와 관련된 구현 내용
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            if (mCamera == null) {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            }
        } catch (IOException e) {
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        // View 가 존재하지 않을 때
        if (mCameraHolder.getSurface() == null) {
            return;
        }

        // 작업을 위해 잠시 멈춘다
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // 에러가 나더라도 무시한다.
        }

        // 카메라 설정을 다시 한다.
        Camera.Parameters parameters = mCamera.getParameters();
        List<String> focusModes = parameters.getSupportedFocusModes();
        if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        mCamera.setParameters(parameters);

        // View 를 재생성한다.
        try {
            mCamera.setPreviewDisplay(mCameraHolder);
            mCamera.startPreview();
        } catch (Exception e) {
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
}
저작자 표시 비영리 변경 금지
신고
Posted by 녹두장군


티스토리 툴바