검색결과 리스트
글
안드로이드 Framework에서 Camera 동작 원리 분석(2)
지난 시간에 이어 Framework 단계에서 Camera가 CameraService와 어떻게 연결되는지 이어서 진행해 보도록 하겠습니다.
Application 단계에서 Camera 클래스의 open() 매소드를 실행한 후 이를 지속적으로 추적하여 CameraBase 까지 접근하였고 이 곳에서 CameraService와의 접점을 찾는 과정까지 진행하였습니다.
/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 |
이제 여기서부터 한 줄씩 분석을 하며 내려가 보도록 하겠습니다.
sp<TCam> c = new TCam(cameraId);
먼저 저 Templete인 TCam의 정체를 확인해 보도록 합시다. TCam은 위 함수가 호출되기 전 미리 정의가 되어 있을 것입니다.
/frameworks/av/camera/Camera.cpp
1 2 3 4 5 6 7 | #include <camera/Camera.h> sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName, int clientUid) { return CameraBaseT::connect(cameraId, clientPackageName, clientUid); } | cs |
TCam을 알아보려 함수를 거슬러 올라왔더니 이번에는 CameraBaseT가 떡하니 있군요. 이 녀석은 무엇을 하는 녀석일까요?
/frameworks/av/include/camera/Camera.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <camera/CameraBase.h> class Camera : public CameraBase<Camera>, public BnCameraClient { public: enum { USE_CALLING_UID = ICameraService::USE_CALLING_UID }; // construct a camera client from an existing remote static sp<Camera> create(const sp<ICamera>& camera); static sp<Camera> connect(int cameraId, const String16& clientPackageName, int clientUid); } | cs |
/frameworks/av/include/camera/CameraBase.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); typedef CameraBase<TCam> CameraBaseT; } | cs |
함수 하나를 설명하려다 보니 생각보다 많은 코드들이 추가되었군요. 일단 위 함수들을 근거로 하나씩 단서를 찾아보도록 합시다. 우선 CameraBaseT의 정체를 알아봅시다.
return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
바로 위의 CameraBase.h 파일 내에서 CameraBaseT가 typedef로 정의되어 있는 것을 확인하실 수 있습니다. 이를 변환해 보면 다음과 같군요.
return CameraBase<TCam>::connect(cameraId, clientPackageName, clientUid);
그래도 아직 TCam의 정체는 풀리지 않았군요 그러나 이는 Camera.h 에 정의된 Camera의 상속 클래스를 확인하면 바로 풀리는 것을 보실 수 있습니다.
class Camera : public CameraBase<Camera>, public BnCameraClient
이로서 TCam의 정체는 Camera라는 것을 알게 되었습니다. 이를 다시 정리하면 다음과 같이 변환될 수 있음을 알 수 있습니다.
sp<Camera> c = new Camera(cameraId);
/frameworks/av/camera/Camera.cpp
1 2 3 4 | Camera::Camera(int cameraId) : CameraBase(cameraId) { } | cs |
/frameworks/av/include/camera/CameraBase.h
1 2 3 4 5 6 7 | template <typename TCam, typename TCamTraits = CameraTraits<TCam> > class CameraBase : public IBinder::DeathRecipient { protected: CameraBase(int cameraId); const int mCameraId; } | cs |
/frameworks/av/camera/CameraBase.cpp
1 2 3 4 5 6 | template <typename TCam, typename TCamTraits> CameraBase<TCam, TCamTraits>::CameraBase(int cameraId) : mStatus(UNKNOWN_ERROR), mCameraId(cameraId) { } | cs |
이러한 과정을 통해 Camera 객체가 생성되었음을 확인하였습니다. 이제 다음줄을 보도록 합니다.
sp<TCamCallbacks> cl = c;
자... 이번에는 TCamCallbacks가 뿅 하고 나타나 우리들을 괴롭히고 있군요! 이 녀석의 정체는 과연 누구일까요?
/frameworks/av/include/camera/Camera.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <camera/CameraBase.h> template <> struct CameraTraits<Camera> { typedef CameraListener TCamListener; typedef ICamera TCamUser; typedef ICameraClient TCamCallbacks; typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&, int, const String16&, int, /*out*/ sp<ICamera>&); static TCamConnectService fnConnectService; }; | cs |
/frameworks/av/include/camera/CameraBase.h
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } | cs |
위 코드를 처음 볼 땐 그냥 정신이 멍해질 겁니다. 하지만 다시 정신 바짝 차리시고 코드를 상세하게 살펴보도록 합니다!
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
우선 CameraBase.h 헤더에서 다음과 같이 CameraTraits<Tcam> 이 정의되어 있고 이는 struct 내의 CameraTraits에 적용이 됩니다.
/frameworks/av/include/camera/Camera.h
....
....
위의 CameraTraits<Camera> 로 선언된 struct로 인해 TCam이 Camera임을 알 수 있습니다. 또한 ICameraClient 클래스가 TCamCallbacks로 선언되어 있는 모습을 보실 수 있습니다.
이제 문제는 CameraBase.h에 정의된 아래의 코드가 뜻을 의미하기 난해다다는 점이지요.
typedef typename TCamTraits::TCamCallbacks TCamCallbacks;
저 위에 쓰인 typename은 template 내에서 쓰이는건 많이 보았는데 여기서는 클래스 내에까지 정의되어 있는 것을 보실 수 있습니다. 이는 위에 보이시는 TCamTraits라는 것이 class임을 컴파일에게 알려주기 위해 부득이하게 typename을 적어준 것이라고 이해해 주시면 되겠습니다. 이와 관련해서 좀 더 자세한 정보를 알고 싶으신 분께서는 아래 링크를 참조해 주시기 바랍니다.
위의 코드를 우리들이 읽기 쉽게 변환하면 다음과 같다고 보실 수 있겠습니다.
typedef typename TCamTraits::TCamCallbacks TCamCallbacks;
typedef CameraTraits<Camera>::TCamCallbacks TCamCallbacks;
typedef CameraTraits<Camera>::ICameraClient TCamCallbacks;
CameraTraits<Camera>::ICameraClient
결국 TCamCallbacks는 ICameraClient와 같음을 확인하실 수 있습니다.
sp<ICameraClient> cl = c;
이제 다음 코드를 확인해 보도록 하겠습니다.
const sp<ICameraService>& cs = getCameraService();
ICameraService 클래스를 불러오라는 듯한 의미의 코드로 해석됩니다. 여기서 getCameraService() 함수를 살펴보도록 하겠습니다.
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 | // establish binder interface to camera service template <typename TCam, typename TCamTraits> const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService() { Mutex::Autolock _l(gLock); if (gCameraService.get() == 0) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { binder = sm->getService(String16(kCameraServiceName)); if (binder != 0) { break; } ALOGW("CameraService not published, waiting..."); usleep(kCameraServicePollDelay); } while(true); if (gDeathNotifier == NULL) { gDeathNotifier = new DeathNotifier(); } binder->linkToDeath(gDeathNotifier); gCameraService = interface_cast<ICameraService>(binder); } ALOGE_IF(gCameraService == 0, "no CameraService!?"); return gCameraService; } |
위 코드는 BpCameraService와 BpBinder를 불러들이는 코드입니다. 위 코드에 대해 자세한 내용은 아래 포스팅을 참고해주시기 바랍니다.
위 과정까지 진행하셨다면 CameraBase와 CameraService가 Binder를 통해 연결되었음을 확인하실 수 있습니다. 이제 다음으로 이후의 코드들을 살펴보도록 하겠습니다.
/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 |
이제 이후의 코드들을 한 줄씩 분석해 보도록 하겠습니다.
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
이 코드는 typedef로 정의된 함수 포인터 변수를 선언하고 이것에 실행하고자 하는 함수를 저장하고 있습니다. fnConnectService 내에는 다음과 같은 내용의 함수가 저장됩니다.
/frameworks/av/camera/Camera.cpp
1 2 | CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService = &ICameraService::connect; | cs |
fnConnectService 함수 포인터는 ICamaerService의 connect 함수를 저장함을 확인할 수 있습니다. typedef 함수 포인터에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조하시기 바랍니다.
status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
위에서 설정하였던 함수 포인터를 실행하는 코드입니다. sp<T> 클래스에서 get() 함수를 호출하면 자신이 가지고 있는 객체값의 포인터 값을 불러옵니다. 이 때 리턴값의 자료형은 T입니다. 위의 코드에서 변수 cs는 ICameraService를 리턴합니다. 이 때 cs에는 BpCameraService를 저장하고 있으므로 BpCameraService::connect 함수를 실행하게 된다.
/frameworks/av/camera/ICameraService.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 | class BpCameraService: public BpInterface<ICameraService> { public: BpCameraService(const sp<IBinder>& impl) : BpInterface<ICameraService>(impl) { } .... // connect to camera service (android.hardware.Camera) virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16 &clientPackageName, int clientUid, /*out*/ sp<ICamera>& device) { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeStrongBinder(cameraClient->asBinder()); data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); remote()->transact(BnCameraService::CONNECT, data, &reply); if (readExceptionCode(reply)) return -EPROTO; status_t status = reply.readInt32(); if (reply.readInt32() != 0) { device = interface_cast<ICamera>(reply.readStrongBinder()); } return status; } .... }; | cs |
Proxy 측인 BpCameraService 클래스에서 실행된 Parcel에 대한 transact() 함수는 이후 Native 측인 BnCameraService 클래스로 Parcel을 통해 값이 넘어오면서 실행되기 시작합니다.
/frameworks/av/camera/ICameraService.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 | status_t BnCameraService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { .... case CONNECT: { CHECK_INTERFACE(ICameraService, data, reply); sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); int32_t cameraId = data.readInt32(); const String16 clientName = data.readString16(); int32_t clientUid = data.readInt32(); sp<ICamera> camera; status_t status = connect(cameraClient, cameraId, clientName, clientUid, /*out*/ camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { reply->writeInt32(1); reply->writeStrongBinder(camera->asBinder()); } else { reply->writeInt32(0); } return NO_ERROR; } break; .... } } | cs |
위의 코드를 보았을 때 마치 connect() 함수가 이전에 실행했던 BpCameraService 클래스의 connect() 함수를 실행하는 듯한 모습을 보이고 있습니다. 하지만 사실은 Native 프로세스의 CameraService 내의 connect() 함수가 실행되고 있다는 것을 기억하시면 되겠습니다. 해당 코드를 분석해보도록 합시다.
/frameworks/av/services/camera/libcameraservice/CameraService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CameraService : public BinderService<CameraService>, public BnCameraService, public IBinder::DeathRecipient, public camera_module_callbacks_t { .... virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ sp<ICamera>& device); .... } | cs |
먼저 CameraService 클래스의 구조를 보겠습니다. 위에서 보시면 아시듯이 BnCameraService 클래스를 상속받고 있는 것을 보실 수 있으며 CameraService 클래스 내에 connect() 함수가 virtual로 선언되어 있는 모습을 확인하실 수 있습니다.
1 2 3 4 5 6 7 8 9 10 | class BnCameraService: public BnInterface<ICameraService> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; }; // namespace android | cs |
BnCameraService 클래스는 보시듯이 BnInterface를 상속받고 있으며 BnInterface는 ICameraService 상속하는 상황임을 보실 수 있습니다.
/frameworks/native/include/binder/IInterface.h
1 2 3 4 5 6 7 8 9 10 | template<typename INTERFACE> class BnInterface : public INTERFACE, public BBinder { public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; protected: virtual IBinder* onAsBinder(); }; | cs |
여기까지 확인하셨다면 일단 CameraService 클래스의 상속상황이 어떻게 되어있는지 어느정도 감을 잡으셨으리라 생각합니다.
/frameworks/av/include/camera/ICameraService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class ICameraService : public IInterface { .... public: /** * clientPackageName and clientUid are used for permissions checking. if * clientUid == USE_CALLING_UID, then the calling UID is used instead. Only * trusted callers can set a clientUid other than USE_CALLING_UID. */ virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ sp<ICamera>& device) = 0; .... } | cs |
자, 이제 저 connect() 함수는 어느 부분에서 선언되어 있는 것인지 확인해 보도록 합시다. C++에서 virtual 함수는 Java에서 Dynamic 함수로 정의하는 것으로 감을 잡으시면 이해가 쉬울 것입니다.
이에 대한 힌트는 이전에 작성하였던 포스팅 중에 있습니다. 바로 CameraService가 등록되는 과정입니다.
/frameworks/native/include/binder/BinderService.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 | #ifndef ANDROID_BINDER_SERVICE_H #define ANDROID_BINDER_SERVICE_H #include <stdint.h> #include <utils/Errors.h> #include <utils/String16.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> // --------------------------------------------------------------------------- namespace android { template<typename SERVICE> class BinderService { public: static status_t publish(bool allowIsolated = false) { sp<IServiceManager> sm(defaultServiceManager()); return sm->addService( String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); } static void publishAndJoinThreadPool(bool allowIsolated = false) { publish(allowIsolated); joinThreadPool(); } static void instantiate() { publish(); } static status_t shutdown() { return NO_ERROR; } private: static void joinThreadPool() { sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->joinThreadPool(); } }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BINDER_SERVICE_H | cs |
내용이 다소 생략되어 있습니다만 위의 addService() 과정에서 CameraService 클래스가 new를 통해 생성되고 있음을 보실 수 있습니다. 즉, CameraService가 SystemService에 등록될 때 순수 CameraService 클래스가 선언되어 있으므로 virtual 함수의 dynamic 성질을 생각한다면 CameraService 내의 함수가 호출되어야 함이 맞음을 알 수 있을 것입니다.
/frameworks/av/services/camera/libcameraservice/CameraService.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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | status_t CameraService::connect( const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ sp<ICamera>& device) { String8 clientName8(clientPackageName); int callingPid = getCallingPid(); LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, clientName8.string(), cameraId); status_t status = validateConnect(cameraId, /*inout*/clientUid); if (status != OK) { return status; } sp<Client> client; { Mutex::Autolock lock(mServiceLock); sp<BasicClient> clientTmp; if (!canConnectUnsafe(cameraId, clientPackageName, cameraClient->asBinder(), /*out*/clientTmp)) { return -EBUSY; } else if (client.get() != NULL) { device = static_cast<Client*>(clientTmp.get()); return OK; } int facing = -1; int deviceVersion = getDeviceVersion(cameraId, &facing); // If there are other non-exclusive users of the camera, // this will tear them down before we can reuse the camera if (isValidCameraId(cameraId)) { // transition from PRESENT -> NOT_AVAILABLE updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, cameraId); } switch(deviceVersion) { case CAMERA_DEVICE_API_VERSION_1_0: client = new CameraClient(this, cameraClient, clientPackageName, cameraId, facing, callingPid, clientUid, getpid()); break; case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: client = new Camera2Client(this, cameraClient, clientPackageName, cameraId, facing, callingPid, clientUid, getpid(), deviceVersion); break; case -1: ALOGE("Invalid camera id %d", cameraId); return BAD_VALUE; default: ALOGE("Unknown camera device HAL version: %d", deviceVersion); return INVALID_OPERATION; } status_t status = connectFinishUnsafe(client, client->getRemote()); if (status != OK) { // this is probably not recoverable.. maybe the client can try again // OK: we can only get here if we were originally in PRESENT state updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId); return status; } mClient[cameraId] = client; LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid()); } // important: release the mutex here so the client can call back // into the service from its destructor (can be at the end of the call) device = client; return OK; } | cs |
현재 과정에서 보시는 바와 같이 client 에는 CameraClient() 혹은 Camera2Client() 함수가 저장되었고 이는 Camera 클래스 내의 mCamera에 저장됩니다.
위 과정까지 마치게 된다면 카메라가 CameraService와 연결이 완료가 되면서 Camera 클래스 객체를 android_hardware_Camera.cpp 에서 실행하고 있던 초기화 함수로 return하게 됩니다.
다음 포스팅에서는 android_hardware_Camera.cpp 에서 진행하고 있던 초기화 함수 부분에서 이어서 진행하도록 하겠습니다.
'안드로이드 > 카메라' 카테고리의 다른 글
안드로이드 Camera의 Framework 구조 (0) | 2015.08.27 |
---|---|
안드로이드 Framework에서 Camera 동작 원리 분석(4) (0) | 2015.05.01 |
안드로이드 Framework에서 Camera 동작 원리 분석(3) (0) | 2015.04.27 |
안드로이드 Framework에서 Camera 동작 원리 분석(1) (2) | 2015.03.11 |
Kitkat 이후의 버전에서 SrufaceView를 활용하여 Camera 활용하기 (0) | 2015.02.04 |