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

안드로이드/카메라 2015.04.27 00:04

 안드로이드 카메라 기능을 꾸준히 분석해 보고 있습니다만 생각보다 복잡한 구조에 놀라우면서도 한 편으로는 안드로이드의 심오함을 동시에 느끼고 있습니다. 이번 포스팅은 지난시간에 이어 계속 이어가도록 하겠습니다.


 지난 포스팅까지 Camera의 Connect() 함수가 동작하는 과정에 대해 살펴보았었습니다. 이번 포스팅에서는 바로 그 다음부터 이어가도록 하겠습니다.


/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
// 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<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);


JNICameraContext 클래스를 선언하는 모습입니다. JNICameraContext 클래스의 내용을 자세히 살펴보도록 합시다.


/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
41
42
43
44
45
46
47
48
49
// provides persistent context for calls from native code to Java
class JNICameraContext: public CameraListener
{
public:
    JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
    ~JNICameraContext() { release(); }
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata);
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
    void postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata);
    void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType);
    void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
    sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
    bool isRawImageCallbackBufferAvailable() const;
    void release();
 
private:
    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
    void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers);
    void clearCallbackBuffers_l(JNIEnv *env);
    jbyteArray getCallbackBuffer(JNIEnv *env, Vector<jbyteArray> *buffers, size_t bufferSize);
 
    jobject     mCameraJObjectWeak;     // weak reference to java object
    jclass      mCameraJClass;          // strong reference to java class
    sp<Camera>  mCamera;                // strong reference to native object
    jclass      mFaceClass;  // strong reference to Face class
    jclass      mRectClass;  // strong reference to Rect class
    Mutex       mLock;
 
    /*
     * Global reference application-managed raw image buffer queue.
     *
     * Manual-only mode is supported for raw image callbacks, which is
     * set whenever method addCallbackBuffer() with msgType =
     * CAMERA_MSG_RAW_IMAGE is called; otherwise, null is returned
     * with raw image callbacks.
     */
    Vector<jbyteArray> mRawImageCallbackBuffers;
 
    /*
     * Application-managed preview buffer queue and the flags
     * associated with the usage of the preview buffer callback.
     */
    Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[]
    bool mManualBufferMode;              // Whether to use application managed buffers.
    bool mManualCameraCallbackSet;       // Whether the callback has been set, used to
                                         // reduce unnecessary calls to set the callback.
};
cs


 이 클래스는 Camera 관련 호출이 Native에서 Java로 전송해야 될 때 주로 쓰이는 클래스로 추측할 수 있습니다. 일단 자세한 사항은 다음에 기회가 될 때 살명하고 넘어가도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
{
    mCameraJObjectWeak = env->NewGlobalRef(weak_this);
    mCameraJClass = (jclass)env->NewGlobalRef(clazz);
    mCamera = camera;
 
    jclass faceClazz = env->FindClass("android/hardware/Camera$Face");
    mFaceClass = (jclass) env->NewGlobalRef(faceClazz);
 
    jclass rectClazz = env->FindClass("android/graphics/Rect");
    mRectClass = (jclass) env->NewGlobalRef(rectClazz);
 
    mManualBufferMode = false;
    mManualCameraCallbackSet = false;
}
cs


 위에서 보시는 바와 같이 JNICameraContext 클래스 내에 Camera와 관련된 Java 클래스값들을 선언하고 있는 것을 확인하실 수 있습니다.



context->incStrong((void*)android_hardware_Camera_native_setup);


함수 android_hardware_Camera_native_setup() 함수의 참조계수를 1 증가시킵니다.


camera->setListener(context);


위에서 선언하였던 JNICameraContext의 변수값을 camera 클래스 변수 내에 Listerner로 설정해줍니다.


/frameworks/av/camera/Camera.cpp

1
2
3
4
5
void Camera::setListener(const sp<CameraListener>& listener)
{
    Mutex::Autolock _l(mLock);
    mListener = listener;
}
cs


 이로서 대망의 Camera.open() 함수의 동작과정을 (1)~(3) 포스팅을 통해 모두 살펴보았습니다. Camera의 구현과정을 처음 보시는 분들이시라면 상당히 큰 어려움이 있으실 것이라 생각합니다. 막히더라도 일단 망설이지 마시고 대략 이러한 기능을 한다는 것을 기억하신 후 다음 단계로 넘어가신다면 이후 넘어갔던 부분이 이해가 되실 날이 오리라 저는 생각합니다!