검색결과 리스트
글
안드로이드 Framework에서 Camera 동작 원리 분석(3)
안드로이드 카메라 기능을 꾸준히 분석해 보고 있습니다만 생각보다 복잡한 구조에 놀라우면서도 한 편으로는 안드로이드의 심오함을 동시에 느끼고 있습니다. 이번 포스팅은 지난시간에 이어 계속 이어가도록 하겠습니다.
지난 포스팅까지 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의 구현과정을 처음 보시는 분들이시라면 상당히 큰 어려움이 있으실 것이라 생각합니다. 막히더라도 일단 망설이지 마시고 대략 이러한 기능을 한다는 것을 기억하신 후 다음 단계로 넘어가신다면 이후 넘어갔던 부분이 이해가 되실 날이 오리라 저는 생각합니다!
'안드로이드 > 카메라' 카테고리의 다른 글
| 안드로이드 Camera의 Framework 구조 (0) | 2015.08.27 |
|---|---|
| 안드로이드 Framework에서 Camera 동작 원리 분석(4) (0) | 2015.05.01 |
| 안드로이드 Framework에서 Camera 동작 원리 분석(2) (3) | 2015.04.26 |
| 안드로이드 Framework에서 Camera 동작 원리 분석(1) (2) | 2015.03.11 |
| Kitkat 이후의 버전에서 SrufaceView를 활용하여 Camera 활용하기 (0) | 2015.02.04 |
설정
트랙백
댓글
글
안드로이드 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 |
설정
트랙백
댓글
글
Canon MG2900 시리즈 무선 프린터 설치방법
https://elecs.tistory.com/342 간만에 프린터를 구매하면서 이왕이면 무선 인터넷을 통한 프린팅 기능이 있는 프린터를 사고자 하여 Canon PIXMA MG2990을 구매하게 되었습니다.
오늘날이 스마트 시대인 만큼 프린터도 무선 기능을 지원하는 추세이기도 한데요
컴퓨터에 직접 연결해서 쓸 때는 설명서 내용에 충실하면 문제없이 사용이 가능합니다.
그런데 무선 프린터의 가장 큰 불편한 점은
공유기 설정 관련 지식이 해박하지 않으신 분들은
설치과정에서 어려운 점이 한두가지가 아니라는 겁니다.
특히 저의 경우 MG2990에서 지원하는 WPS 설치를 진행하다 피를 봤으니 말이지요.
그런고로 여기서는 Canon사의 MG2900 시리즈를 구매하신 분들을 대상으로 무선으로 프린터를 설치하는 과정에 대해 알아보고자 합니다.
2019년 10월 11일 추가 - 만약 이 글에서 소개된 무선 설치 방법이 정상적으로 진행되지 않으시는 분들께서는 아래의 링크와 같이 USB를 연결하여 프린터를 설치해보시길 바랍니다.
1. 먼저 동봉된 DVD를 삽입하신 후 설치 파일인 MSetup4.exe 파일을 실행합니다.
만약 자신의 컴퓨터에 광매체 드라이브(ODD)가 없어 동봉된 DVD를 사용하지 못하시는 분은 아래 사이트를 통해 설치 파일을 다운로드 받아 진행하시기 바랍니다.
다음과 같은 화면이 나오면 우선 설치 프로그램이 실행된 것입니다. 다음 버튼을 눌러 설치를 진행합니다.
곧바로 설치 프로그램이 무선공유기를 통해 프린터를 찾는 모드에 진입합니다.
만약 자신의 공유기가 프린터와 연결되어 있다면 곧바로 드라이버 설치 모드에 진입될 것입니다.
'무선 LAN 연결'을 선택하신 후 '다음'버튼을 클릭합니다.
여기서는 무선 공유기를 통해 연결할 예정이므로 '액세스 포인트 연결(권장)'을 선택하신 후 '다음' 버튼을 클릭합니다.
당연히 처음에는 공유기가 프린터와 연결되어 있지 않으므로 다음과 같은 화면이 나오는 것을 보실 수 있습니다. '다음' 버튼을 클릭합니다.
다음과 같은 화면이 진행 된 후 설치가이드 모드에 들어가게 됩니다.
'다음' 버튼을 클릭합니다.
설치 환경이 총 3가지가 주어져 있습니다.
이 중 가장 쉬운 방법은 WPS를 사용하는 방법입니다만
이게 몇몇 공유기는 자칫하면 프린터가 공유기의 설정을 제멋대로 바꾸어 버리기 때문에
잘못하면 다른 기기들이 Wi-Fi를 사용하지 못하는 경우가 발생합니다.
여기서는 오른쪽 빨간색으로 표시한 부분 PIN 코드 (방법)을 통해 진행하겠습니다.
위에서 설명하고 있는 바와 같이
자신의 공유기 설정을 확인하셔서 자신의 공유기 환경이 어떻게 구성되어 있는지 살펴봅니다.
만약 자신의 공유기가 'IEEE802.11n 전용'으로 설정되어 있다면 이를 다른 것으로 설정해 주시면 되겠습니다.
이를 위해 자신의 공유기에 접속하여 설정을 변경해야 하는데
거의 대부분의 공유기는 다음 주소를 통해 설정모드에 진입하실 수 있습니다.
http://192.168.1.1/
종종 다른 공유기의 경우 설정 모드에 진입하는 방식이 다른 경우가 있습니다. 자세한 사항은 자신의 공유기 제작사의 홈페이지를 참조하시기 바랍니다.
자신의 프린터에 전원을 넣은 후 위의 그림과 같이 중지(Stop) 버튼을 꾸욱 누르고 기다립니다.
그러면 알람(Alarm) 림프가 깜빡거리기 시작하는데 이것이 15회 깜빡이는 것을 확인하시면 바로 버튼에서 손을 땝니다. 그러면 아래와 같이 프린트 물이 출력되는 것을 확인하실 수 있습니다.
이렇게 자신의 프린터에 설정된 정보들이 프린터에 출력됨을 확인하실 수 있습니다.
위에서 진행했던 것과 같이 다시 프린터의 중지(Stop) 버튼울 꾸욱 누르고 기다립니다.
이번에는 알람(Alarm) 버튼이 16번 깜빡거리기를 기다리다가 떼어주시면 전원과 무선 부분 두 램프가 함께 깜빡이는 것을 보실 수 있습니다.
위와 같은 화면을 보셨다면 곧바로 자신의 공유기 설정 모드에 진입하셔서 위의 과정에서 출력했던 프린트물 내에 적혀진 PIN코드를 자신의 공유기에 입력해줍니다.
아래는 netis사 공유기의 펌웨어 화면으로 'WPS 설정'을 누르신 후 '장비 추가' 버튼을 클리갛면 아래부분에 '새로운 장비 추가' 메뉴가 나타납니다. 여기서 '연결할 무선 장비의 PIN코드 입력' 을 선택한 후 출력된 인쇄지에 적힌 PIN코드를 입력하신 후 '연결' 버튼을 클릭하시면 됩니다.
이 과정이 성공적으로 끝난다면 프린터의 전원과 Wi-Fi 버튼의 깜빡임이 멈추게 될 것입니다.
위의 화면과 같이 네트워크 프린터 목록에서 자신의 프린터 기기가 공유기와 연결되었음을 확인하실 수 있습니다. 자신의 프린터를 마우스로 클릭한 후 '다음'버튼을 클릭하시면 드라이버가 본격적으로 설치되기 시작합니다.
만약 위의 화면과 같은 결과가 나오지 않으신 분들은 '다음'버튼을 누르신 후 위의 과정을 한번 더 반복합니다.
위와 같은 화면을 보셨다면 드디어 자신의 컴퓨터에 드라이버가 실치될 것입니다!
'흔치않은일상' 카테고리의 다른 글
| 박근혜 대통령 탄핵 가결, 국회의사당 주변 풍경 [2016.12.09] (0) | 2016.12.10 |
|---|---|
| 기업 탐방기록 - SAP 코리아 방문기[2016.01.12] (0) | 2016.01.15 |
| Wi-Fi 프린팅 지원 Canon PIXMA MG2990 개봉기 (0) | 2015.04.17 |
| 세월호 참사 1주기 합동분향소 주변풍경 (0) | 2015.04.16 |
| 구글 애드센스에 로그인 했더니... (0) | 2015.04.03 |
