본문 바로가기

2013
임베디드월드

글: 라영호 | ratharn@naver.com / 2013-01-04


[연재 차례]

1. 안드로이드 시스템의 역사 및 동향
2. 안드로이드 시스템과 리눅스
3. 안드로이드 플랫폼의 이해
4. 안드로이드 바인더(Binder)의 이해
5. 안드로이드 서비스
6. 안드로이드 SurfaceFlinger와 프레임버퍼 드라이버
7. 안드로이드 User Interface와 ADK2012
8. Linux Sound Device와 안드로이드 사운드 시스템
9. 안드로이드 카메라 시스템
10. 안드로이드 카메라와 멀티미디어 프레임워크
11. 안드로이드 카메라와 멀티미디어 프레임워크 ②
12. 안드로이드 시스템 디버깅 및 기타



안드로이드 시스템 개발자를 위한 안드로이드 시스템의 분석 및 이해 ⑩
안드로이드 카메라와 멀티미디어 프레임워크

2013년 1월호에서는 지난 2012년 12월호에서 다루던 내용 중 마지막 부분인 안드로이드에서 시스템의 카메라에서 서비스 동작부분과 카메라 및 각종 코덱등과 연관되어 멀티미디어를 처리하게 되는 멀티미디어 프레임워크에 대해 확인해 보도록 하겠다. 현재 멀티미디어 프레임워크는 스마트폰 및 임베디드 시스템에 장착된 필수적인 시스템으로 사진뿐만 아니라 동영상, DMB와 같은 장치를 처리하는데 있어 필수 장치로 포함되고 있다. 다른 장치와 달리 하드웨어적인 도움을 많이 받는 장치이기 때문에 단순히 단독으로 동작하는 것 보다는 LCD를 다루는 프레임 버퍼 드라이버, 카메라 장치등과 연관되어 있고 멀티미디어 정보를 처리할 수 있는 멀티미디어 코덱과 관계가 있다. 이러한 내용에 대해서 안드로이드 내부에서 어떻게 처리하고 있는지 살펴보도록 하겠다.

카메라 서비스
frameworks/base/camera/libcameraservice/의 디렉토리의 파일들은 카메라 서비스의 구현을 담당한다. 카메라 서비스는 ICameraService를 상속받음으로써 실제 서비스가 이루어진다.
frameworks/base/camera/libcameraservice/Android.mk에서 USE_CAMERA_STUB 이란 변수를 이용하여 실제 카메라를 사용할 것인지 아닌지 결정한다. 만일 이 변수가 true 라면 CameraHardware Stub.cpp, FakeCamera.cpp 파일을 컴파일하고 false 면 CameraService.cpp 만 사용해서 카메라 서비스를 구성하게 된다.

frameworks/base/camera/libcameraservice/

ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=false
ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
USE_CAMERA_STUB:=true
endif #libcamerastub
Endif
ifeq ($(USE_CAMERA_STUB),true)
# libcamerastub
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=   ₩
  CameraHardwareStub.cpp   ₩
  FakeCamera.cpp
LOCAL_MODULE : = libcamerastub
LOCAL_SHARED_LIBRARIES : = libui
include $(BUILD_STATIC_LIBRARY)
endif # USE_CAMERA_STUB
# libcameraservice
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=  
  CameraService.cpp

frameworks/base/camera/libcameraservice/CameraService.h

class CameraService : public BnCameraService
{
  class Client;
    중략. . .
  class Client : public BnCamera {}; // BnCamera로부터 상속 받음
    중략. . .
  mutable Mutex  mServiceLock;
    wp<Client> mClient;
}


카메라 서비스의 시작

frameworks/base/camera/libcameraservice/CameraService.cpp

  void CameraService::instantiate() {
    defaultServiceManager()->addService(
    String16("media.camera"), new CameraService());
    }


[그림 1] 카메라 서비스의 순서도

CameraService::instatiate() 함수에서 등록되는 “media.camera” service는 framwork/base/libs/ ui/Camera.cpp의 Camera::getCameraService()에서 호출되는 Service의 이름과 일치한다. 카메라 서비스는 Camera framework에서의 server 역할을 맡는 부분이다. 카메라 서비스는 시스템 초기 프로세서인 init process에 의해 분석되고 실행이 되는 init.rc의 내용인 mediaserver가 실행이 되면서 시작이 된다. mediaserver는 android의 media관련 service daemon이며 camera, audio, mediaplayer 세 개에 대한 서비스를 담당한다.

defaultServiceManager()->addService() 를 이용하여 서비스 매니저에 등록이 되며, 이 후에는 서비스 매니저의 관리를 받게 된다. media service의 경우는 등록될 수 있는 서비스 이름이 서비스 매니저에 의해 지정이 되어 있으며 다른 이름이 등록이 될 경우는 permission denied가 발생이 된다.

Camera 서비스의 내부 동작
Camera.cpp는 Camera::getCameraService()를 통해서 ICameraService 를 호출한다.
실제는 BpCameraService가 호출될 때 이루어진다. BpCameraService는 binder를 통해서 두 프로세스 사이의 BnCameraService 통신채널을 생성한다. BpCamera Service의 connect()함수에서는 다음과 같이 BnCamera Service를 연결시키는 부분이 있다.

remote()->transact(BnCameraService::CONNECT, data, &reply);


Camera server로의 서비스 요청
카메라 서비스로의 접속은 서비스 매니저로부터 Camera Service를 담당하는 binder class인 ICamera Service를 get함으로써 이루어진다. 물론, 그 전에 ICameraClient 형의 Camera class를 생성함으로써 Camera client로부터 Camera server(카메라 서비스)로의 Binder 연결이 이루어진다.

CONNECT command를 통해서 카메라 서비스로 접속을 하게 되면 카메라 서비스 쪽에서는 server에서 client로 접속되는 ICamera 형의 client class를 생성해서 Camera client 쪽으로 할당해 준다. ICameraService를 이용한 Binder interface는 CONNECT command외에 다른 command는 존재하지 않는다. 이 Class는 오직 connect를 받아서 새로운 Binder를 생성(server에서 client로의 data 통로를 여는)하기만 할 뿐이다.

위의 메커니즘 설명은 이러한 부분에 대한 기술적인 설명을 하는 부분이 된다. IPC인 Binder 부분은 실제 호출되는 부분과 실행되는 부분이 감추어져 있기 때문에 코드에 대한 추적이 어렵다.
즉, ICameraService.cpp의 BpCameraService의 connect()를 통해, CameraService.cpp의 Camera

Service::onTransact() 로 아래의 내용을

status_t err = BnCameraService::onTransact(code, data, reply, flags);

호출함으로써 두 개의 프로세스간 BnCameraService()통신채널을 구성하게 된다. 실제 BpCameraService의 구현은 CameraServicce에서 이루어진다고 보면 된다. Camera.cpp는 다른 프로세스로 동작 하지만, 그 인터페이스는 ICameraService의 connect()를 호출함으로써 CameraService의 ICamera class인 CameraService:: Client를 획득할 수 있게 된다. Camera에 관련된 함수들은 CameraService:: Client 가 생성되면서 실제로 구현된다.

frameworks/base/camera/libcameraservice/CameraService.cpp

CameraService::Client::Client(const sp<CameraService> & cameraService,
  const sp<ICameraClient>& cameraClient, pid_t clientPid) {
  int callingPid = getCallingPid();
  LOGD("Client::Client E (pid %d)", callingPid);
  mCameraService = cameraService;
  mCameraClient = cameraClient;
  mClientPid = clientPid;
  mHardware = openCameraHardware(); // Hardware open함수를 호출한다.
  mUseOverlay = mHardware->useOverlay();
  mMediaPlayerClick = newMediaPlayer(“/system/ media/audio/ui/camera_click.ogg”);
  mMediaPlayerBeep = newMediaPlayer(“/system/ media/audio/ui/VideoRecord.ogg”);
   // Callback is disabled by default
  mPreviewCallbackFlag = FRAME_CALLBACK_ FLAG_NOOP;
    cameraService->incUsers();
    LOGD("Client::Client X (pid %d)", callingPid);
}

CameraService::Client::Client()는 ICamera type을 생성한다. 여기에서는 openCamerahardware를 호출한다. CameraHardwareInterface Class의 형태를 갖는CameraHardwareStub.cpp의 함수를 직접 호출한다. CameraHardwareStub.h와 CameraHardware Stub.cpp에서 실제 카메라 함수들이 구현된다.
여기에서 구현된 모듈이 실제 인터페이스를 담당한다. 실제 카메라가 없을 경우 카메라 하드웨어를 사용할 경우에는 CameraHardwareStub.cpp에서는 시뮬레이터를 사용하게 된다. 시뮬레이터에 대한 코드는 FakeCamera. cpp에 있다.


CameraHardwareStub Class에 대한 정의

frameworks/base/camera/libcameraservice/CameraHardwareStub.h

class CameraHardwareStub : public Camera HardwareInterface {
  . . .
  class PreviewThread : public Thread {
    CameraHardwareStub* mHardware;
  . . .
    };   };

CameraHardwareStub Class는 PreviewThread 멤버를 갖고 있다. 이 스레드는 프리뷰를 담당하는 기능을 한다. 카메라 실제 하드웨어 인터페이스는 V4L2 API를 이용한다. JPEG 인코딩 작업을 통해서 데이터를 JPEG 파일로 저장할 수 있다. CameraHardwareStub의 openCamera hardware()함수를 통해서 카메라 하드웨어를 초기화하는 과정을 진행한다. 카메라 서비스의 클라이언트를 하나 생성하면서, 카메라 하드웨어를 오픈 하게 된다.

frameworks/base/camera/libcameraservice/CameraService.cpp
CameraService::Client::Client(const sp<Camera Service>& cameraService,
  const sp<ICameraClient>& cameraClient, pid_t clientPid)
{
  int callingPid = getCallingPid();
  LOGD("Client::Client E (pid %d)", callingPid);
  mCameraService = cameraService; ;
  mCameraClient = cameraClient;
  mClientPid = clientPid;
  mHardware = openCameraHardware(); // 하드웨어 오픈 함수를 호출한다.
    mUseOverlay = mHardware->useOverlay();
  mMediaPlayerClick = newMediaPlayer(“/system/ media/audio/ui/camera_click.ogg”);
  mMediaPlayerBeep = newMediaPlayer(“/system/ media/audio/ui/VideoRecord.ogg”);
    // Callback is disabled by default
  mPreviewCallbackFlag = FRAME_CALLBACK_ FLAG_NOOP;
    cameraService->incUsers();
    LOGD(“Client::Client X (pid %d)”, callingPid);
}


카메라 하드웨어 동작 구조


[그림 2] 카메라 하드웨어에 대한 초기화 과정에 대한 그림

카메라 하드웨어 오픈 동작에 대한 전체적인 구조는 [그림2]와 같다.

frameworks/base/camera/libcameraservice/CameraHardwareStub.cpp

sp<CameraHardwareInterface> Camera HardwareStub:: createInstance(){
  if (singleton != 0) {
    sp<CameraHardwareInterface> hardware = singleton.promote();
  if (hardware != 0) {
  return hardware;   }   }
  sp<CameraHardwareInterface> hardware(new CameraHardwareStub());
  singleton = hardware;
  return hardware;}
extern “C” sp<CameraHardwareInterface> openCameraHardware(){
  return CameraHardwareStub::createInstance();
}


안드로이드 멀티미디어
OpenCORE는 Google Android의 Multimedia Framework로서 PacketVideo라고도 불린다. Packet Video는 회사이름이기도 하며, OpenCORE Multimedia Framework는 PacketVideo를 포함한 Software Layer의 이름이기도 하다. Google Android의 다른 라이브러리들과 비교했을 때 OpenCORE Multimedia Framework 코드는 매우 크고, C++로 작성된 Full-Featured 운영체제에 통합되는 구조로 되어 있다.

OpenCORE Multimedia Framework를 거시적인 관점에서 볼 때, 그것은 주로 다음과 같은 두 가지 측면을 포함하고 있다.

OpenCORE Multimedia Framework는 다음 그림과 같이 여러 레벨로 나눌 수 있다.


[그림 3] OpenCORE의 구조 그림

OSCL(Operating System Compatibility Library)
운영 체제 호환성 라이브러리. 다른 운영체제간의 호환성을 위하여 기본 운영체제 동작을 지원하는 기능을 포함하고 있다. 기본 데이터 형식, Configuration, String Instruments, I/O, Error handling, Thread 등을 포함한 C++ 기본 라이브러리와 유사하다.

PVMF (PacketVideo Multimedia Framework)
Document Analysis(Parser)와 Composition (Composer)를 구현한 Framework. 이 안의 Codec Node는 공통적인 인터페이스를 상속할 수 있다. 사용자 계층은 Node를 생성하기 위하여 그 공통 인터페이스를 상속 할 수 있다.

PVPlayer 엔진 및 PVAuthor 엔진
Player의 입장에서 PVPlayer는 입력(Source)으로 Network File 혹은 Media Stream 등이 될 수 있고, 출력(Sink)은 오디오/비디오 장비의 입력이 될 수 있고, 기본적인 기능을 포함하는 미디어 흐름 제어와 Document Analysis, Video Streaming, Audio Decoder(Decode)와 그 외의 다른 특징을 갖고 있다. Paper Document서부터 방송 미디어까지 포함하고 있고, 또한 Network-Related RTSP Streaming 기능도 포함하고 있다.

미디어 영역의 recording에 있어서, PVAuthor 입력 (원본)은 카메라, 마이크 및 기타 장비, 출력(Sink) 각종 문서의 동기화의 흐름, 비디오(Encode)로 작성된 문서 등 오디오 인코딩 스트리밍 등 기능을 수행한다.
OpenCORE SDK의 사용에 있어, 응용프로그램 계층에서 연결계층을 구현하는 것이 필요하고, 그 연결계층은 PVMF를 위한 Node의 특정기능을 Common Interface를 이용해서 구현해야 하는데 이것은 상위 단에서의 사용을 위하여 Plug-in 형태로 구현하게 된다.

OpenCORE 코드 구조
OpenCORE의 코드는 /external/opencore 디렉토리에 있으며 여기에 있는 코드들을 참조하면 된다.

코드 설명
/external/opencore/android 안드로이드의 다른 part들과 OpenCORE간의 인터페이스를 담당
/external/opencore/baselibs Data container들을 위한 기본 라이브러리를 담음(MIME 스트링 제어, 쓰레드 간의 메시지 처리)
/external/opencore/codecs_v2 audio, video codec에 대한 코드들 audio, video 코덱들은 OpenMAX IL Component 상태로 구현되어 있다.
/external/opencore/engines
/external/opencore/engines/2way
/external/opencore/engines/adapters
/external/opencore/engines/common
/external/opencore/engines/player
/external/opencore/engines/author
PVAuthor, PVPlayer 등의 엔진 소스들, PVPlayer 운용의 핵심적인 기능들에 대한 구현
/external/opencore/extern_libs_v2 OpenCORE가 쓰는 3rd party 라이브러리들이 들어있다. 현재 이 디렉토리는 Khronos OpenMax IL 인터페이스를 사용하는 헤더파일이 있음
/external/opencore/fileformats Mp4/3gp,mp3,wav, aac 등 다양한 파일 포맷을 파싱하는 라이브러리 내포
/external/opencore/nodes PVMF가 제공하는 Node에 대한 코드가 있다. 멀티미디어 처리 unit에 독립적으로 실행될 수 있도록 추상화 되어 있음
/external/opencore/oscl OSCL(Operating System Compatible Library)에 대한 코드
/external/opencore/pvmi PacketVideo Multimedia infrastructure. OpenCORE를 이루는 근간이 되는 곳  
/external/opencore/protocol HTTP, RTP/RTCP, RTSP, SDP 같은 다양한 네트워크 프로토콜을 위한 파서들과 컴포저들 내포
/external/opencore/tools_v2 안드로이드의 바깥쪽 라이브러리를 build 하기 위해 사용되는 compiler tool들과 등록될 수 있는 모듈들이 있음


OpenCORE 라이브러리
OpenCore를 구성하는 기본적인 라이브러리 및 내용은 다음과 같다.

libopencore_common.so - 다양한 기본 라이브러리를 제공
libopencore_net_support.so - 네트워크를 지원하는 함수들을 제공한다.

libopencore_player.so 및 libopencore_author.so -각각 playback과 recording에 사용이 되고, 이 라이브러리들은 외부 인터페이스를 담당하게 된다. 많은 형태의 다양한 plug-in(Plug-in) 방식의 player를 추가하는 방법으로는 두 개의 함수가 사용이 되는데, 하나는 등록에 사용이 되고, 다른 하나는 실제 구현코드가 들어있다.

libopencore_common.so - 라이브러리는 전체 Open CORE library의 핵심.
libopencore_common.so - 라이브러리에는 다음과 같은 내용이 포함되어 있다.

OSCL
Framework 중 PVMF 관련 내용 (pvmi / pvmf / Android.mk)
기본적인 라이브러리 (baselibs)
Codec 관련 내용
Output node (nodes / pvfileoutputnode / Android.mk)

이 라이브러리는 밑에는 OSCL을 가지고 있고, 이와 마찬가지로 Node tool과 codec을 지원하는 PVMF Framework에 대한 기능도 가지고 있다.
libopencore_player.so 는 다음과 같은 것을 포함하고 있다.

decoding tool
file parser(mp4)
Node corresponding decoding tool
player engine part (engines / player / Android.mk)
Android's player adapter for the (android / Android.mk)
identification tools (pvmi / recognizer)
codec part OpenMax tools (codecs_v2/omx)
Node corresponding to the registration of a number of plug-ins

libopencore_player.so의 주요 내용은 각 parser와 decoder 관련 내용들이다. PVPlayer는 engines/player/ Android.mk 에 그 핵심 함수들이 있다. android/Android. mk 에는 pvplayer를 android 용 player로 사용하기 위하여 PVPlayer를 Android에 built-in 시키는 코드가 있다.
libopencore_author.so 는 다음과 같은 기능을 갖고 있다.

Encoding tools (video streaming H263, H264, audio stream Amr)
The composition of device files (mp4)
Node corresponding encoding tools
Media input Node (nodes / pvmediainputnode / Android.m)
Author part of the engine (engines / author / Android.mk)
The author for Android adapter (android / author / Android.mk)

libopencore_author.so 라이브러리는 각 component에 대한 encoder와 muxer가 있다.
PVAuthor는 engines/author/Android.mk 에 그 핵심 함수들이 있고, android/Android.mk 에는 media recorder로 사용하기 위하여 PVAuthor를 Android에 built-in 시키는 코드가 있다.

Network support library - libopencore_net_support.so :
MP4 functions library - libopencore_mp4local.so
MP4 registration library - libopencore_mp4localreg.so :
RTSP functions library - libopencore_rtsp.so
RTSP Registration library - libopencore_rtspreg.so :
Download 관련 library - libopencore_download.so
Download registration library - libopencore_ downloadreg.so :

OSCL
OSCL(operating system-compatible library)은 다양한 운영체제에 대해 포팅이 가능하도록 구성되어 있고, 그 code structure는 다음과 같다.

oscl/oscl
|-- config : configuration macro
|-- osclbase : 기본적인 데이터 타입, macro, STL container와 비슷한 함수들이 존재
|-- osclerror : error-handling functions
|-- osclio : file IO and Socket functions
|-- oscllib : dynamic library interface function
|-- osclmemory : memory management(auto pointer등과 같은 기능 포함)
|-- osclproc : thread, multi-task communications and other functions
|-- osclregcli : client 등록에 대한 함수
|-- osclregserv : 서버 등록관련 함수들
`-- osclutil : string처리와 같은 기본 유틸리티 함수들

OSCL 디렉터리는 각 모듈 별 디렉터리로 나누어져 있다. OSCL에는 관련된 함수들이 아주 자세하게 설명되고 있으며, C 언어를 주로 사용하며, 상위 단에서의 사용에 있어서는 C++ 인터페이스가 사용이 된다. 사실 OpenCORE에서의 PVMF의 사용은 OSCL에 의존적이며, 전체 OpenCORE에 대한 호출은 OSCL 부분을 사용하는 게 필요하다
OSCL의 구현에 있어 많은 전형적인 C 함수는 간단한 패키지의 형태로 존재하게 된다. 예를 들어 수학함수와 관련된 osclutil의 해당 부분은 inline 함수의 형태로 oscl_mach.inl 파일에 존재하게 된다.

CODE:
OSCL_COND_EXPORT_REF OSCL_INLINE double oscl_log(double value)
{
return (double) log(value);
}
OSCL_COND_EXPORT_REF OSCL_INLINE double oscl_log10(double value)
{
return (double) log10(value);
}
OSCL_COND_EXPORT_REF OSCL_INLINE double oscl_sqrt(double value)
{
return (double) sqrt(value);
}

oscl_mach.inl 파일은 oscl_math.h에 include되고, 결과적으로 oscl_log()함수는 log()함수를 사용한 것과 동일하게 된다 C 언어 표준 라이브러리는 C++의 형태로 정의가 되어 있다.
예를 들어서 oscllib 디레토리는 다음과 같은 구조를 가지고 있다. 소스상의 oscl_shared_library.h 는 다음과 같이 선언되어 있다.

oscl/oscl/oscllib/
|-- Android.mk
|-- build
| `-- make
|   |-- local.mk
`-- src
|-- oscl_configfile_list.cpp
|-- oscl_configfile_list.h
|-- oscl_library_common.h
|-- oscl_library_list.cpp
|-- oscl_library_list.h
|-- oscl_shared_lib_interface.h
|-- oscl_shared_library.cpp
`-- oscl_shared_library.h

CODE:
class OsclSharedLibrary
{
public:
OSCL_IMPORT_REF OsclSharedLibrary();
OSCL_IMPORT_REF OsclSharedLibrary(const OSCL_String& aPath);
OSCL_IMPORT_REF ~OsclSharedLibrary();
OSCL_IMPORT_REF OsclLibStatus LoadLib(const OSCL_String& aPath);
OSCL_IMPORT_REF OsclLibStatus LoadLib();
OSCL_IMPORT_REF void SetLibPath(const OSCL_String& aPath);
OSCL_IMPORT_REF OsclLibStatus QueryInterface(const OsclUuid& aInterfaceId, OsclAny*& aInterfacePtr);
OSCL_IMPORT_REF OsclLibStatus Close();
OSCL_IMPORT_REF void AddRef();
OSCL_IMPORT_REF void RemoveRef();
}


끝으로
지금까지 안드로이드 시스템에서 사용하는 카메라 시스템과 멀티미디어 프레임워크에서 살펴봤다. 다음호에서는 멀티미디어 프레임워크의 내부에 대해서 좀더 살펴보고 전원 관리 부분에 대해 살펴보도록 하겠다.


/필/자/소/개/

필자

라영호

국내 스마트폰의 초창기인 Cellvic에서부터 스마트폰을 개발하였고 윈도 모바일 및 다양한 임베디드 시스템을 개발하고 있다. 현재는 안드로이드 시스템 포팅 및 임베디드 시스템 개발, 컨설팅, 교육 등을 진행하는 회사를 운영하고 있다. 개인 블로그(www.embeddedce.com)를 통해 임베디드 시스템개발에 대한 다양한 생각과 방법론을 함께 생각해 보고자 노력 중이다.

※ 본 내용은 (주)테크월드(http://www.embeddedworld.co.kr)의 저작권 동의에 의해 공유되고 있습니다.
    Copyright ⓒ Techworld, Inc. 무단전재 및 재배포 금지

맨 위로
맨 위로