안드로이드 Framework에서 Camera 동작 원리 분석(1)

안드로이드/카메라 2015.03.11 02:06

 안드로이드 프레임워크를 공부하면서 느끼는 점이 애플리케이션 단계에서는 함수 몇 줄만 쓰면 쉽게 구현되는 기능이 프레임워크를 공부하면서 안드로이드 세계의 심오함을 몸소 느끼고 있습니다. 정말로 운영체제를 만든 분들이 존경스러울 정도 입니다.

 이번 포스팅 부터는 안드로이드 카메라가 Framework 단계에서 동작하는 과정에 대해 살펴보도록 하겠습니다. 내용이 상당히 방대하기 때문에 분량을 중간에 나누어 가면서 다루도록 하겠습니다.



 우리들이 Applycation 단계에서 Camera를 사용할 때 보통은 SurfaceHolder를 통해서 Camera를 사용합니다. 혹시 SurfaceHolder의 동작 원리에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주셨으면 합니다.

http://elecs.tistory.com/78


Application 단계에서 Camera 클래스가 사용되는 사례를 보도록 합시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
    private SurfaceHolder.Callback surfaceListener = new SurfaceHolder.Callback() {
        
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            camera.release();
            
        }
        
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            camera = Camera.open();
            try {
                camera.setPreviewDisplay(holder);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
        
        @Override
        public void surfaceChanged(SurfaceHolder holder, int formatint width, int height) {
            // TODO Auto-generated method stub
            Camera.Parameters parameters = camera.getParameters();
            parameters.setPreviewSize(width, height);
            camera.startPreview();
            
        }
    };
cs


 흔히들 위의 방식대로 Camera 클래스를 사용하고 계시리라 생각합니다. 그렇다면 각 함수별로 좀 더 들어가보도록 하겠습니다.


 SurfaceView가 화면에 보이게 되면 SurfaceHolder에서 surfaceCreated()함수가 실행됩니다. 이 때 Camera 관련 기능들이 동작을 시작하게 되는 것이지요.


camera = Camera.open();


 이 부분은 Camera의 기능을 활성화 하는 단계입니다. Camera의 코드 내부를 살펴보면 다음과 같습니다.


/frameworks/base/core/java/android/hardware/Camera.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Camera {
 
    ....
 
    public static Camera open(int cameraId) {
        return new Camera(cameraId);
    }
 
    public static Camera open() {
        int numberOfCameras = getNumberOfCameras();
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                return new Camera(i);
            }
        }
        return null;
    }
 
    Camera(int cameraId) {
        mShutterCallback = null;
        mRawImageCallback = null;
        mJpegCallback = null;
        mPreviewCallback = null;
        mPostviewCallback = null;
        mUsingPreviewAllocation = false;
        mZoomListener = null;
 
        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }
 
        String packageName = ActivityThread.currentPackageName();
 
        native_setup(new WeakReference<Camera>(this), cameraId, packageName);
    }
 
    private native final void native_setup(Object camera_this, int cameraId,
                                           String packageName);
 
    ....
 
 
}
cs


위에서 보시는 바와 같이 JNI를 통해 native_setup()를 호출하는 모습을 보실 수 있습니다. 이번에는 Native 코드를 확인해 보도록 하겠습니다.


/frameworks/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jstring clientPackageName)
{
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);
 
    sp<Camera> camera = Camera::connect(cameraId, clientName,
            Camera::USE_CALLING_UID);
 
    if (camera == NULL) {
        jniThrowRuntimeException(env, "Fail to connect to camera service");
        return;
    }
 
    // make sure camera hardware is alive
    if (camera->getStatus() != NO_ERROR) {
        jniThrowRuntimeException(env, "Camera initialization failed");
        return;
    }
 
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
        return;
    }
 
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong((void*)android_hardware_Camera_native_setup);
    camera->setListener(context);
 
    // save context in opaque field
    env->SetIntField(thiz, fields.context, (int)context.get());
}
 
cs


 이 함수들을 처음 접하시는 분들은 상당한 압박감이 오시리라 생각합니다. 당연히 처음 프레임워크 소스를 분석할 때 뭐가 뭔지 전허 알 길이 없기 때문에 초기에는 해멜 수 밖에 없는 것은 사실입니다. 하지만 겁먹지 않고 차근차근 살펴보다 보면 어느덧 수많은 코드에 익숙해져 있는 자신을 발견하게 될 것입니다.


 위 과정에서 sp<>의 기능에 대해 생소해 하실 분들이 계실 겁니다. 이전 포스팅에서 제가 아는 한에서 나마 설명한 자료가 있으니 해당 자료를 참조해 주셨으면 합니다.

http://elecs.tistory.com/79


다음으로 해당 함수가 실행되는 것을 보실 수 있습니다.

sp<Camera> camera = Camera::connect(cameraId, clientName,

            Camera::USE_CALLING_UID);


이는 Camera.cpp 의 클래스에서 카메라와 연결되는 과정을 다루고 있는 것입니다. 이를 한 번 자세히 보도록 하겠습니다.

/frameworks/av/camera/Camera.cpp

1
2
3
4
5
sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
        int clientUid)
{
    return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}
cs

 여기까지 쉼없이 달려왔건만 이번에는 CameraBaseT라는 함수가 튀어나오는군요!


/frameworks/av/include/camera/CameraBase.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
template <typename TCam>
struct CameraTraits {
};
 
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public:
    typedef typename TCamTraits::TCamListener       TCamListener;
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
 
    static sp<TCam>      connect(int cameraId,
                                 const String16& clientPackageName,
                                 int clientUid);
    virtual void         disconnect();
 
    void                 setListener(const sp<TCamListener>& listener);
 
    static int           getNumberOfCameras();
 
    static status_t      getCameraInfo(int cameraId,
                                       /*out*/
                                       struct CameraInfo* cameraInfo);
 
    static status_t      addServiceListener(
                                    const sp<ICameraServiceListener>& listener);
 
    static status_t      removeServiceListener(
                                    const sp<ICameraServiceListener>& listener);
 
    sp<TCamUser>         remote();
 
    // Status is set to 'UNKNOWN_ERROR' after successful (re)connection
    status_t             getStatus();
 
protected:
    CameraBase(int cameraId);
    virtual              ~CameraBase();
 
    ////////////////////////////////////////////////////////
    // TCamCallbacks implementation
    ////////////////////////////////////////////////////////
    virtual void         notifyCallback(int32_t msgType, int32_t ext,
                                        int32_t ext2);
 
    ////////////////////////////////////////////////////////
    // Common instance variables
    ////////////////////////////////////////////////////////
    Mutex                            mLock;
 
    virtual void                     binderDied(const wp<IBinder>& who);
 
    // helper function to obtain camera service handle
    static const sp<ICameraService>& getCameraService();
 
    sp<TCamUser>                     mCamera;
    status_t                         mStatus;
 
    sp<TCamListener>                 mListener;
 
    const int                        mCameraId;
 
    typedef CameraBase<TCam>         CameraBaseT;
};
 
cs

/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
 
    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}
cs


 드디어 가장 중요한 CameraService와 IPC통신을 할 수 있는 부분까지 나온 것을 확인하게 되었습니다. Camera에서 Open() 함수를 좇다 CameraService와의 연계점을 직접 보게 된 상황입니다.


 우선 여기까지 잘 따라와 주신 분이라면 Camera 클래스가 IPC로 연결되기 바로 직전의 부분에 이르렀음을 말씀드리고 싶네요. 다음 시간에는 CameraService와 Camera가 연결되는 과정을 살펴보도록 하겠습니다.


안드로이드 Framework에서 Camera 동작 원리 분석(2)

http://elecs.tistory.com/102

  • 궁금 2016.12.10 03:55 ADDR 수정/삭제 답글

    안녕하세요!
    게시글을 보는 중에 궁금증이 생겨서요!
    갑자기 c++ 파일과 언어가 나오는데..
    안드로이드는 JAVA로만 개발가능하지않나요??

    • Justin T. 2016.12.10 10:33 신고 수정/삭제

      반갑습니다.
      사실상 모든 기기들은 C를 기반으로 동작하며 안드로이드 또한 C를 기반으로 동작합니다.
      안드로이드의 경우 dalvic 혹은 ART 기반의 java 언어를 쓰는 가상머신 위에서 Application을 제작하기 때문에 java만으로도 애플리케이션을 만드는 것이 가능합니다.
      그러나 가상머신의 한계로 인해 연산속도가 native보다 너무 느리기 때문에 연산 속도가 중요한 Application의 경우 JNI를 통해 C로 연산 알고리즘을 개발하는 겁니다