본문 바로가기

개발/android

안드로이드 카메라 공부하기

반응형

카메라를 공부하려다 보니 여차저차 여기까지 오게 되었다...


OpenGL : 2D,3D 그래픽 표준 규격 API 프로그래밍 언어간 플래폼 간의 교차 응용 프로그램 지원


OpenGl ES: 임베디드 시스템을 위한 API

각종 라이브러리들, 예제들 확인 해보면 2.0 기준으로 작성 됐던 것들이 많았다

다만 최근엔 3.1 버전도 지원하고 있다 (최소 빌드 Kitkat 이상 버전) 

최소 JellyBean 기준이라면 ES3.0


EGL: 안드로이드에서 OS에 접근 하여, 윈도우 생성 및 접근하게 해주는 라이브러리 

렌더링 API와 네이티브 플랫폼의 윈도우 시스템 간의 인터페이스

그래픽 Context 관리, Surface, Buffer 바인딩, 렌더링 동기화 처리 등을 한다 


GL Context:  OpenGL 인스턴스의 상태와 관련되어 있다 

프레임 버퍼 오브젝트를 그리는데 쓰이는 기본 프레임 버퍼를 표현한다

한문장으로 OpenGL의 모든것을 붙잡고 있는 오브젝트


EGL Surface: EGL에 의해 할당된 OFF-SCREEN 버퍼(called pbuffer) 으로 만들거나, OS가 할당한 윈도우로 만들 수 있다

EGL Surface는 eglCreateWindowSurface() 함수로 생성된다

EGL Surface를 사용해서 GLES로 그림을 그리거나, 비디로 디코더 결과를 쓰는데 사용 할 수 있다


GLSurfaceView 를 쓰면 더 쉽게 EGL Context 를 관리하고, 이래저래 쉽게 사용할 수 있지만, GLES를 위한 필수는 아니다


GL ES는 그래픽 렌더링 API를 정의하지만, 윈도우 시스템을 정의하지는 않는다. 따라서 윈도우를 생성, 접근 시켜주는 해당 OS 관련 라이브러리가 필요하다

안드로이드에서는 EGL 이 이 역할을 한다


안드로이드에서 ES 함수를 사용하려면  GL Context가 필요하다


1. 먼저 EGL Context를 생성한다 

2. EGL Surface 생성한다

-> eglCreateWindowSurface() 로 생성한다 . 이 함수는 window 객체를 인자로 받는데 

--> SurfaceView, SurfaceTexture, SurfaceHolder, Surface 클래스로 만든 인스턴스가 될 수 있다

위의 인스턴스들은 내부에 BufferQuere를 가지고 있고 

이 함수를 호출하면 EGL은 

EGLSurface 인스턴스를 생성하고 

이 인스턴스를 Window 인스턴스에 포함된 BufferQueue의 생산자 인터페이스에  연결한다

3. eglswapBuffers() 함수로 현재 프레임을 전송한다

- 한번에 하나의 EGLSurface와 하나의 Surface 가 연결 될 수 있다

- 스레드는 여러 EGLSurface 중에 Current(EGLContext) 가 가르키는 것을 변경하여 EGLSurface를 변경 할 수 있다

- EGLSurface와 Surface는 유사한 개념이 아닌 독림된 개념이다. Surface없이도 EGLSurface에 그림을 그릴 수 있고 EGL없이 Surface를 쓸 수 있다 


---------------------------------------------------------------------------------------------------------------------------------------

안드로이드에서 모든 그래픽 요소 중심에는 BufferQueue 클래스가 있다 

BufferQueue :  어디에, 어떻게 쓰는가

넓이, 높이, 픽셀 포멧, 용도 등의 값을 가진 생산자를 만들고, 

생산자가 빈 버퍼를 요청(dequeBuffer()) 한 다음 

값을 넣어서 큐에 반환(queueBuffer())한다

그 후 소비자는 버퍼를 얻고(aquireBuffer())

다 쓰면 해제한 후 버퍼큐에 반환한다(releaseBuffer())


생산자가 드로잉 명령어를 보낸 후, 렌더링 완료 전 출력 버퍼를 큐에 넣고, fence값이 오면 바로 해제하는 동기 framework류를 쓰면 성능이 향상 된다고 한다 

(sync framework 부분은 추후 더 해보기로)


* Buffer의 내용은 BufferQueue에 의해 절대 복사되지 않는다. 항상 핸들러를 통해 전달된다

 

Double Buffering: back buffer가 처리되는 동안 front buffer가 디스플리에되고, back 버퍼가 준비되면 빠르게 교환한다 

대개 그래픽용 버퍼는 트리플 버퍼로 구성된다

SCREEN Record 기능

https://source.android.com/devices/graphics/arch-sf-hwc



Surface : BufferQueue의 생산자 역할

SurfaceFlinger : 보통은 버퍼 큐의 소비자 역할

Surface 에 렌더링 -> 버퍼 형태 생성 -> 소비자 전달


lockCanvas() :버퍼에 락을 걸고 그리기위한  Canvas를 리턴한다 

unlockanvas() : 버퍼 락을 해제하고 compositor로 전달한다


아무것도 안그리고 lockCanvas, unlockCanvas 반복 시, 이전 렌더링 했던 프레임이 반복된다

Canvas를 이용하지 않고, Surface에 직접 드로잉 할 때 OPENGL ES가 사용된다 

SurfaceHolder: surface 관련 작업을 할 때에는 SurfaceHolder, 특히 SurfaceView가 필요하다

surface와 관련된 파라미터들을 얻거나 설정 할 때에는 SurfaceHolder를 통해 구현된다


레이아웃과 뷰는 갯수에 상관없이 하나의 버퍼에 렌더링된다

SurfaceView가 렌더링 할 때가되면 컨텐츠들은 완전 투명하게 된다.(시스루처럼 영역이 변함)


SurfaceView의 뷰 컴포넌트가 시각화 되려고 하면, 프레임워크는 WindowManager에게 SurfaceFlingr가 새 Surface를 생성하도록 요청한다

(비동기적이기 때무에 Surface생성 완료 시 콜백을 사용해야 한다)



SurfaceView 렌더링시, APP이 아닌 SurfaceFlinger에 의해 그려진다(App과는 별도의 윈도우 영역)

생산자(Surface) -> bufferQueue -> 소비자(SurfaceFlinger)


만약 GLES로 surface렌더링을 하고 있다면 SurfaceHolder#setFixedSize()함수로 크기 설정이 가능하다 

(만약 더 알고 싶으면 Grafika, Hardware scaler exerciser 액티비티 를 보자)


SurfaceTexture : Surface + GLES Texture 조합 

SurfaceTexture 가 생성한 새 버퍼를 inqueue 하면, 앱은 onFrameAvailable을 통해 통지 받는다 SurfaceTexture의 updateTextImage() 를 호출하면 이전에 가진 버퍼를  해제하고 새로운 버퍼를 받은 다음, 이 버퍼를 GLES에서 외부 TEXTURE로 사옹 가능하겍 수행한다 (결국 소비자 역할)


SurfaceTexture가 BufferQueue를 생성 시, usage_flag를 GRALLOC_USAGE_HW_TEXTURE로 설정하면 galloc에 의해 생성된 버퍼는 GLES가 인식 할 수 있다


만약 내가 setPreviewTexture()로 카메라 출력을 BufferQueue 생산자 인터페이스에 연결한다면, 비디오를 만들 때,  PTS를 설정할 필요가 있다


애플리케이션이 Surface를 생성하는 방법은 SurfaceTexture 를 인자로 받는 Surface() 생성자 호출 뿐이다

(SurfaceTexture의 내부적인 이름은 GLConsumer이다 그만큼 소비자의 역할이 크다는 의미)









참고 

https://ggeutzzang.tistory.com/10?category=642827

https://www.khronos.org/opengl/wiki/OpenGL_Context

https://source.android.com/devices/graphics/architecture.html

반응형