공유기에 연결된 Linux(Ubuntu) 컴퓨터를 외부에서 원격 데스크톱 연결을 통해 조작하기

공대생의 팁 2015. 5. 7. 16:51

최근에는 노트북의 성능도 꽤 좋아진 편이라 한 대만 들고다녀도 어지간한 데스크탑 컴퓨터의 기능은 무리없이 수행할 수 있게 되었습니다만 그럼에도 아직까지는 데스크톱의 성능을 따라가지는 못하는 것이 현실이지요. 그렇다고 해서 데스크탑 컴퓨터를 이리저리 들고다닐 수는 없는 노릇이지요.


 이러한 경우를 위해 최근의 운영체제에서는 다른 컴퓨터를 통해 원격으로 조작이 가능한 기능이 구현되어 있는데요 이 원격 기능이 서로 다른 운영체제끼리도 호환이 된다는 점이 있습니다. 그렇기에 자신의 노트북 컴퓨터가 Windows라 하더라도 자신의 집에 있는 데스크탑이 Ubuntu라면 원격접속을 통해 데스크탑 PC를 마치 내 노트북에서 사용하는 듯이 원거리에서 조작이 가능합니다!


 이번 포스팅에서는 공유기와 연결되어 있는 Ubuntu 운영체제 데스크톱을 야외 등 외부에서 인터넷을 통해 접속하여 데스크톱을 원격조작하는 방식에 대해 알아보고자 합니다. 그럼 시작해보도록 하겠습니다.


1. 먼저 Ubuntu에 다음 명령어를 입력하여 프로그램을 설치합니다.


$ sudo apt-get install vnc4server xrdp


4. 설치한 프로그램을 바로 적용합니다.


$ sudo service xrdp restart


 만약 자신의 컴퓨터가 다른 포트가 설정되어 xrdp와 충돌하게 될 경우 설정을 변경해야 되는 경우가 있습니다. 이는 /etc/xrdp/xrdp.ini 와

/etc/xrdp/sesman.ini 파일을 수정해 주시면 되겠습니다.


/etc/xrdp/xrdp.ini

 위 화면의 설정을 보았을 때 현재 [globals]에서 포트번호가 기본으로 3389번으로 설정되어 있습니다. 만약 자신의 환경에서 해당 포트를 다른 곳에서 사용하고 있거나 자신이 설정하고자 하는 다른 포트번호가 있으면 해당 부분을 변경해 주시면 되겠습니다.


3. 아래 명령어를 입력하여 포트가 제대로 설정되었는지 확인합니다.


$ netstat -antp




 위의 명령어를 통해 화면에 나오는 것처럼 자신이 설정한 포트가 LISTEN으로 설정되어 있다면 외부에서 접속을 받을 준비가 되었다는 의미입니다.


 4. 외부의 원격 조정 요청을 처리하는 부분을 설정해줍니다. Windows버튼 (혹은 우분투 왼쪽 상단 버튼)을 누르신후 한글로 '데스크톱'이라고 입력하시면 '데스크톱 공유'라는 이름의 프로그램이 나타납니다. 해당 프로그램을 실행합니다.



 5. 실행시 '데스크톱 공유 기본 설정'창이 나타납니다. 해당 설정을 아래와 같이 해줍니다. 다음으로 외부에서 접속하였을 때 입력할 비밀번호를 설정해주신 후 닫기 버튼을 클릭합니다.



 여기까지 진행하셨다면 Ubuntu 데스크톱에서 설정은 모두 끝났습니다. 만약 자신의 데스크톱PC가 인터넷선과 직접적으로 연결되어 있다면 자신의 IP 주소만 기억해두시면 바로 원격조정이 가능합니다만 공유기의 경우 포트포워딩을 통해 공유기와 연결된 데스크탑 PC로 연결될 수 있도록 해주어야 합니다. 포트포워딩에 대해 좀 더 자세히 알고 싶신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.


WF2411 공유기를 통한 외부 기기와 소켓 통신 프로그래밍

http://elecs.tistory.com/45


 이번에는 자신의 공유기에 포트포워딩을 설정해줍니다. 본 포스팅에서는 Netis사의 WF2411 공유기를 기준으로 설명하겠습니다. 자신의 공유기를 통한 포트포워딩 설정에 대한 자세한 사항은 공유기 제조사 홈페이지를 참조해주시길 바랍니다.


 6. 현재 자신의 PC에 설정된 IP 주소를 확인합니다. 이 과정을 통해 확인하게 되는 IP주소는 공유기가 자신과 연결된 기기에 할당한 가상 IP로서 공유기와 연결된 기기 사이에서만 사용될 수 있는 내부 IP 주소입니다.


$ ifconfig




 Terminal을 통해 현재 Ubuntu 데스크탑 컴퓨터가 공유기로부터 192.168.1.9 번의 주소를 할당 받고 있음을 확인하실 수 있습니다. 이 주소를 기억하신 후 포트포워딩 설정시 적용하도록 합니다.


 7. 자신의 공유기 설정 모드로 로그인합니다. 일반적으로 공유기 설정 모드에 접속하는 주소는 http://192.168.1.1 입니다.



 8. 포트포워딩 메뉴에 들어가신 후 자신의 컴퓨터 환경에 맞추어 접속 포트를 설정해줍니다.


이곳에서 입력하셔야 할 내용은 다음과 같습니다.

내부 IP주소(서버 PC) - 자신의 Ubuntu 데스크탑이 공유기로부터 할당받은 주소값을 입력합니다.

포트번호(외부) - 원격 접속을 할 컴퓨터가 공유기에 접속하게 될 때 사용할 포트 번호를 입력합니다.

포트번호(내부) - 공유기가 Ubuntu 데스크탑으로 접속하게 될 때 사용할 포트번호를 입력합니다. Ubuntu xrdp의 경우 내부 포트 설정 기본값이 3389로 설정되어 있습니다.


 여기서 외부 포트번호는 원격접속을 하게 될 컴퓨터가 접속하고자 하는 포트 번호를 입력하고 외부에서 해당 포트번호를 입력하게 되면 공유기는 해당 신호를 내부 IP주소가 설정된 컴퓨터로 내부 포트번호를 통해 접속하게 됩니다. 자세한 사항은 아래 그림을 참조해주시면 되겠습니다.

이미지 출저 : http://documentation.commvault.com/hds/v10/article?p=features/firewall/port_forward_gateway.htm


 443번과 444번은 외부 포트번호를 나타내고 있으며 해당 포트 번호로 공유기에 연결을 시도하면 공유기는 이를 해당 색깔과 같은 색깔로 내부 IP주소가 설정된 대로 신호를 전송합니다. 이 때 해당 신호는 내부 포트번호가 440으로 설정된 상황입니다.


  9. 이제 자신의 공유기가 외부로부터 할당받은 IP 주소를 확인합니다. 이 또한 자신의 공유기 설정 페이지에서 확인이 가능합니다.


 위에서 보셨을 때 WAN IP 주소 부분에 적혀있는 것이 바로 자신의 공유기가 외부에서 할당받은 주소입니다. 앞으로 우리는 위 주소를 통해여 원격 데스크톱에 접속할 것입니다.


 이번에는 외부의 컴퓨터를 통해 Ubuntu 데스크톱 컴퓨터를 조종해 보도록 하겠습니다. 먼저 시작(윈도) 버튼을 누르신 후 검색창에 '원격 데스크톱' 을 검색합니다.



 '원격 데스크톱 연결' 프로그램을 실행하신 후 Ubuntu 데스크탑과 연결된 공유기의 IP 주소를 입럭하신 후 포트포워딩으로 설정해 두었던 포트번호를 입력하신 후 '연결' 버튼을 클릭합니다.



 노란 경고 화면이 등장하면서 연결 여부를 확인합니다. '예(Y)'버튼을 클릭합니다.



 아래와 같은 화면이 나온다면 Ubuntu 데스크탑에 연결되는 데에 성공한 것입니다. Module을 'console'로 선택하신 후 우분투에서 '데스크톱 공유 기본설정'시 설정한 비밀번호를 입력합니다.



 올바른 비밀번호를 입력하시게 되면 아래와 같이 원격으로 접속된 Ubuntu 데스크탑 화면이 등장하게 됩니다. 이제 여러분들께서 하고자 하는 작업을 원격으로 수행해 보시기를 바랍니다~!






300x250

TIZEN 소스코드 빌드 gbs가 설치되지 않을 때 수동으로 설치하기

공대생의 팁 2015. 5. 2. 10:48

 Tizen 개발에 입문하기 위해 프로그램들을 설치하는 과정을 진행하던 도중 참으로 이상한 문제가 발생했습니다. 다음과 같이 우분투 apt-get reopsitory에 Tizen 관련 패키지들을 설치할 수 있도록 환경을 구성한 후 gbs 설치를 시도하려 하였으나 더이상 진행이 되지 않는 것이었습니다.


$ sudo vim /etc/apt/sources.list
deb http://download.tizen.org/tools/latest-release/Ubuntu_14.04 /
$ sudo apt-get update

$ sudo apt-get install gbs




 위에서와 같이 gbs를 apt-get을 통해 설치를 시도하니 위에 보는 저 3줄만 덩그러니 뜨기만 하고 설치가 더이상  진행이 되지 않는겁니다. 무언가 제 컴퓨터상의 설정이 잘못되어서 그런지는 잘 모르겠으나 일단 gbs를 설치하셔야 TIZEN 개발환경을 사용하실 수 있습니다. 이 경우 본인은 사이트에 직접 접속하여 소스를 다운로드 받았습니다. 그렇다면 그 방법에 대해 자세히 살펴보도록 합시다.


 먼저 아래 주소로 이동합니다. 우분투 운영체제가 12.04일 경우를 기준으로 설명해 드리겠습니다.


 http://download.tizen.org/tools/latest-release/Ubuntu_14.04



 이 사이트에서 우리들이 설치하고자 하는 패키지를 찾아 설치해보도록 하겠습니다. 현재 우리들이 설치하고자 하는 패키지는 gbs로 해당 패키지를 찾아보도록 하겠습니다.

 apt-get에서 자신이 설치하고자 하는 패키지를 찾는 방법은 위의 파일목록 중 'Packages'를 클릭하시면 TIZEN과 관계된 패키지 목록들을 살펴보실 수 있습니다.



 위의 화면에서 보시는 바와 같이 Package 명이 gbs인 패키지의 정보를 찾으실 수 있습니다. 여기서 해당 패키지의 경로가 'Filename' 부분에 자세히 표기되어 있습니다. 이 위치에 있는 패키지를 다운로드 받으신 후 자신의 컴퓨터에서 실행해줍시다.



 처음 패키지를 실행하게 되면 의존성 문제로 인해 설치가 더이상 진행되지 않습니다. 그렇습니다.. apt-get 명령어를 사용하면 관련 의존성 패키지들이 단 한번에 설치되지만 수동으로 설치하게 되는 경우 이러한 의존성 패키지를 일일히 찾아주어야 하는 상당히 번거로운 과정을 거쳐야 하는 것입니다.

 위에서 보이시는 부분에서 '패키지 의존관계가 불충분함:gbs-api(=0.23.2)' 부분에서 해당 패키지를 위 Package 파일에서 해당 패키지를 찾는 과정을 한 번 더 거쳐줍니다.



 이러한 방식으로 조금은 불편하고 번거롭습니다만 의존성 파일을 지속적으로 찾아주시면서 설치를 진행하시면 gbs 패키지를 모두 설치하실 수 있을 것입니다. 모두들 패키지를 모두 설치하시고 TIZEN 개발자로서의 첫 걸음을 밟아보시기를 바랍니다!

300x250

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

안드로이드/카메라 2015. 5. 1. 00:34

 최근 지속적으로 안드로이드 Camera의 동작 원리에 대해 공부를 꾸준히 하고 있습니다만, 생각보다 다루어야 할 내용이 많군요. 처음엔 Application 단계에서 수행되는 3개의 코드만 분석하면 끝일 줄 알았건만 각 코드의 동작을 이해하기 위해 세부적인 공부가 병행되다보니 포스팅을 시작한지 40일이 넘은 지금 시점에서 4번째 포스팅이 진행되게 되었습니다. 참으로 안드로이드의 구조가 우주처럼 방대해 보이는건 왜일까요?


 서론이 너무 길어지는군요. 그럼 지금부터 포스팅을 진행해 보도록 하겠습니다. 그 전에 1번째 포스팅에서 다루었었던 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private Camera camera;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
....        
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(surfaceListener);
        
    }
    
    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

 지난 (1)~(3)번 포스팅에서는 Camera.open(); 코드의 실행 과정을 살펴보았습니다만 Application 단계에서 단 한줄의 실행이 이토록 길게 다루어질 줄은 생각도 못했습니다. 참고로 이후의 포스팅은 지난 포스팅들에서 확인하였던 개념들을 모두 알고 있다는 전제하에 진행되므로 포스팅을 읽던 도중 모르는 개념이 나온다 싶으면 이전 포스팅의 내용을 복습하면서 읽어나가셨으면 합니다.


camera.setPreviewDisplay(holder);


 Camera 클래스 변수에 객체를 초기화 한 후 처음으로 실행되는 함수입니다. 이 함수를 통해 SurfaceHolder의 값이 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
/**
     * Sets the {@link Surface} to be used for live preview.
     * Either a surface or surface texture is necessary for preview, and
     * preview is necessary to take pictures.  The same surface can be re-set
     * without harm.  Setting a preview surface will un-set any preview surface
     * texture that was set via {@link #setPreviewTexture}.
     *
     * <p>The {@link SurfaceHolder} must already contain a surface when this
     * method is called.  If you are using {@link android.view.SurfaceView},
     * you will need to register a {@link SurfaceHolder.Callback} with
     * {@link SurfaceHolder#addCallback(SurfaceHolder.Callback)} and wait for
     * {@link SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)} before
     * calling setPreviewDisplay() or starting preview.
     *
     * <p>This method must be called before {@link #startPreview()}.  The
     * one exception is that if the preview surface is not set (or set to null)
     * before startPreview() is called, then this method may be called once
     * with a non-null parameter to set the preview surface.  (This allows
     * camera setup and surface creation to happen in parallel, saving time.)
     * The preview surface may not otherwise change while preview is running.
     *
     * @param holder containing the Surface on which to place the preview,
     *     or null to remove the preview surface
     * @throws IOException if the method fails (for example, if the surface
     *     is unavailable or unsuitable).
     */
    public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
        if (holder != null) {
            setPreviewDisplay(holder.getSurface());
        } else {
            setPreviewDisplay((Surface)null);
        }
    }
 
    private native final void setPreviewDisplay(Surface surface) throws IOException;
cs

 이번에도 setPreviewDisplay() native 함수가 호출되고 있는 모습을 보실 수 있습니다. SurfaceHolder 내에는 Surface값이 저장되어 있으며 이 곳에 저장되어 있던 Surface 클래스를 holder에 넘겨준다고 생각하시면 될 듯합니다. 혹시 SurfaceHolder가 어떤 방식으로 동작하는 지에 대해 알고 싶으신 분들께서는 아래 링크를 참조해 주시길 바랍니다.


http://elecs.tistory.com/78


/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
static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
{
    ALOGV("setPreviewDisplay");
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0return;
 
    sp<IGraphicBufferProducer> gbp;
    sp<Surface> surface;
    if (jSurface) {
        surface = android_view_Surface_getSurface(env, jSurface);
        if (surface != NULL) {
            gbp = surface->getIGraphicBufferProducer();
        }
    }
 
    if (camera->setPreviewTarget(gbp) != NO_ERROR) {
        jniThrowException(env, "java/io/IOException""setPreviewTexture failed");
    }
}
cs


 드디어 Camera에 Surface가 적용되는 순간이다. 이 부분을 잘 살펴본다면 Camera의 화면과 관련된 소스코드를 볼 수 있을 것으로 기대된다. 당장 확인해 보도록 해보자.


sp<Camera> camera = get_native_camera(env, thiz, NULL);


 이전에 우리들이 Open() 함수를 통해 만들었던 Camera 클래스를 데리고 오는 것으로 보인다. 해당 함수의 구현을 살펴보도록 하자.


/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
struct fields_t {
    jfieldID    context;
    jfieldID    facing;
    jfieldID    orientation;
    jfieldID    canDisableShutterSound;
    jfieldID    face_rect;
    jfieldID    face_score;
    jfieldID    rect_left;
    jfieldID    rect_top;
    jfieldID    rect_right;
    jfieldID    rect_bottom;
    jmethodID   post_event;
    jmethodID   rect_constructor;
    jmethodID   face_constructor;
};
 
static fields_t fields;
 
....
 
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    JNICameraContext* context = 
        reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    if (context != NULL) {
        camera = context->getCamera();
    }
    ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    if (camera == 0) {
        jniThrowRuntimeException(env, "Method called after release()");
    }
 
    if (pContext != NULL) *pContext = context;
    return camera;
}
cs

이제 각 코드를 한 줄씩 살펴보도록 합시다.


JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));


 이전에 Camera를 설정하는 도중에 context 함수를 만들었던 바가 있습니다. 이 때 생성되었던 context의 경우 Smart Pointer의 참조를 강화시켰기 때문에 아직까지 메모리에 남아있습니다. 이를 reinterpret_cast<>를 이용하여 JNICameraContext 클래스를 불러들이도록 합니다.


camera = context->getCamera();


 불러들인 context 내에 있는 camera 클래스를 꺼내옵니다.


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

1
sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
cs

위 과정을 통해 Camera 클래스를 다시 불어들이게 되었습니다.


이제 나머지 코드들을 하나씩 살펴보도록 합시다.


/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
static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
{
    ALOGV("setPreviewDisplay");
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0return;
 
    sp<IGraphicBufferProducer> gbp;
    sp<Surface> surface;
    if (jSurface) {
        surface = android_view_Surface_getSurface(env, jSurface);
        if (surface != NULL) {
            gbp = surface->getIGraphicBufferProducer();
        }
    }
 
    if (camera->setPreviewTarget(gbp) != NO_ERROR) {
        jniThrowException(env, "java/io/IOException""setPreviewTexture failed");
    }
}
cs


surface = android_view_Surface_getSurface(env, jSurface)


 Native 단계에서 Surface 클래스를 생성하는 함수입니다. 함수 내용을 자세하 분석해 보도록 합시다. 그전에 앞서 android_view_Surface.cpp 소스코드가 Java 단계의 Surface와 연결되는 과정에 대해 확인해 보도록 하겠습니다. Java와 C++ 이 JNI로 연결는 과정은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/82


/frameworks/base/core/jni/android_view_Surface.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
int register_android_view_Surface(JNIEnv* env)
{
    int err = AndroidRuntime::registerNativeMethods(env, "android/view/Surface",
            gSurfaceMethods, NELEM(gSurfaceMethods));
 
    jclass clazz = env->FindClass("android/view/Surface");
    gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
    gSurfaceClassInfo.mNativeObject =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject""I");
    gSurfaceClassInfo.mLock =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mLock""Ljava/lang/Object;");
    gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>""(I)V");
 
    clazz = env->FindClass("android/graphics/Canvas");
    gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer""Landroid/graphics/Canvas$CanvasFinalizer;");
    gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
    gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat""I");
 
    clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
    gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
 
    clazz = env->FindClass("android/graphics/Rect");
    gRectClassInfo.left = env->GetFieldID(clazz, "left""I");
    gRectClassInfo.top = env->GetFieldID(clazz, "top""I");
    gRectClassInfo.right = env->GetFieldID(clazz, "right""I");
    gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom""I");
 
    return err;
}
cs


 위 과정을 통해 JNI로 연결된 Java 단계의 Surface와 Native 단계의 android_view_Surface을 이용하여 아래 소스코드를 살펴보도록 합시다.


/frameworks/base/core/jni/android_view_Surface.cpp

1
2
3
4
5
6
7
8
9
10
11
sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) {
    sp<Surface> sur;
    jobject lock = env->GetObjectField(surfaceObj,
            gSurfaceClassInfo.mLock);
    if (env->MonitorEnter(lock) == JNI_OK) {
        sur = reinterpret_cast<Surface *>(
                env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeObject));
        env->MonitorExit(lock);
    }
    return sur;
}
cs

 

그러면 이제 한 줄씩 살펴보도록 합시다.


jobject lock = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mLock);

 Java Object 변수 lock에 Java 클래스 내에 있는 Object 변수 mLock를 저장한다.

 

sur = reinterpret_cast<Surface *>(

                env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeObject));

 

 Surface 변수 sur에 GetIntField()에 설정된 surface의 값을 받는다.

 

이제 마지막으로 IGraphicBufferProducer 클래스가 설정되는 과정을 확인해 보도록 하겠습니다.

 

/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
static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
{
    ALOGV("setPreviewDisplay");
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0return;
 
    sp<IGraphicBufferProducer> gbp;
    sp<Surface> surface;
    if (jSurface) {
        surface = android_view_Surface_getSurface(env, jSurface);
        if (surface != NULL) {
            gbp = surface->getIGraphicBufferProducer();
        }
    }
 
    if (camera->setPreviewTarget(gbp) != NO_ERROR) {
        jniThrowException(env, "java/io/IOException""setPreviewTexture failed");
    }
}
cs

 

gbp = surface->getIGraphicBufferProducer();


  해당 Surface 클래스에 저장되어 있는 IGraphicBufferProducer 클래스의 값을 저장합니다. 이를 카메라에 연결하면 Surface와 연결되는 과정이라고 생각하시면 되겠습니다.


/frameworks/native/libs/gui/Surface.cpp

1
2
3
sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
    return mGraphicBufferProducer;
}
cs


camera->setPreviewTarget(gbp)
 

 Camera 클래스에 IGraphicBufferProducer를 연결합니다.


/frameworks/av/camera/Camera.cpp

1
2
3
4
5
6
7
8
9
// pass the buffered IGraphicBufferProducer to the camera service
status_t Camera::setPreviewTarget(const sp<IGraphicBufferProducer>& bufferProducer)
{
    ALOGV("setPreviewTarget(%p)", bufferProducer.get());
    sp <ICamera> c = mCamera;
    if (c == 0return NO_INIT;
    ALOGD_IF(bufferProducer == 0"app passed NULL surface");
    return c->setPreviewTarget(bufferProducer);
}
cs


/frameworks/av/include/camera/ICamera.h

1
2
3
4
5
6
7
8
9
10
11
12
13
class ICamera: public IInterface
{
    /**
     * Keep up-to-date with ICamera.aidl in frameworks/base
     */
public:
....
    // pass the buffered IGraphicBufferProducer to the camera service
    virtual status_t        setPreviewTarget(
            const sp<IGraphicBufferProducer>& bufferProducer) = 0;
 
....
}
cs


 setPreviewTarget() 함수가 virtual로 선언되어 있으므로 Dynamic 으로 작동됨을 알 수 있습니다. mCamera 값이 어떤 것으로 선언 되어 있는지는 CameraService::Connect()에 잘 표현되어 있습니다.



/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


 위에서 device 변수는 Camera->mCamera 변수를 나타내고 있습니다. 이에 따라 mCamera는 CameraClient의 함수를 실행하고 있음을 눈치채실 수 있으리라 생각합니다.


/frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// set the buffer consumer that the preview will use
status_t CameraClient::setPreviewTarget(
        const sp<IGraphicBufferProducer>& bufferProducer) {
    LOG1("setPreviewTarget(%p) (pid %d)", bufferProducer.get(),
            getCallingPid());
 
    sp<IBinder> binder;
    sp<ANativeWindow> window;
    if (bufferProducer != 0) {
        binder = bufferProducer->asBinder();
        // Using controlledByApp flag to ensure that the buffer queue remains in
        // async mode for the old camera API, where many applications depend
        // on that behavior.
        window = new Surface(bufferProducer, /*controlledByApp*/ true);
    }
    return setPreviewWindow(binder, window);
}
cs


/frameworks/av/services/camera/libcameraservice/api1/CameraClient.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
status_t CameraClient::setPreviewWindow(const sp<IBinder>& binder,
        const sp<ANativeWindow>& window) {
    Mutex::Autolock lock(mLock);
    status_t result = checkPidAndHardware();
    if (result != NO_ERROR) return result;
 
    // return if no change in surface.
    if (binder == mSurface) {
        return NO_ERROR;
    }
 
    if (window != 0) {
        result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
        if (result != NO_ERROR) {
            ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
                    result);
            return result;
        }
    }
 
    // If preview has been already started, register preview buffers now.
    if (mHardware->previewEnabled()) {
        if (window != 0) {
            native_window_set_scaling_mode(window.get(),
                    NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
            native_window_set_buffers_transform(window.get(), mOrientation);
            result = mHardware->setPreviewWindow(window);
        }
    }
 
    if (result == NO_ERROR) {
        // Everything has succeeded.  Disconnect the old window and remember the
        // new window.
        disconnectWindow(mPreviewWindow);
        mSurface = binder;
        mPreviewWindow = window;
    } else {
        // Something went wrong after we connected to the new window, so
        // disconnect here.
        disconnectWindow(window);
    }
 
    return result;
}
cs


 분량이 다소 길어진 관계로 다음 포스팅에서 내용을 이어가도록 하겠습니다.


300x250