SurfaceTexture
는 노출 영역과 OpenGL ES(GLES) 텍스처의 조합입니다. SurfaceTexture
는 GLES 텍스처로 출력되는 노출 영역을 제공하는 데 사용됩니다.
SurfaceTexture
에는 앱이 소비자인 BufferQueue
의 인스턴스가 포함되어 있습니다. onFrameAvailable()
콜백은 생산자가 새 버퍼를 대기열에 등록하면 이를 앱에 알립니다. 그러면 앱은 updateTexImage()
를 호출하여 이전에 보관된 버퍼를 해제하고 대기열에서 새 버퍼를 획득한 후 EGL을 호출하여 버퍼를 GLES에 외부 텍스처로 제공합니다.
외부 GLES 텍스처
외부 GLES 텍스처(GL_TEXTURE_EXTERNAL_OES
)가 기본 GLES 텍스처(GL_TEXTURE_2D
)와 다른 점은 다음과 같습니다.
- 외부 텍스처는
BufferQueue
에서 수신한 데이터에서 직접 텍스처 다각형을 렌더링합니다. - 외부 텍스처 렌더러는 기존 GLES 텍스처 렌더러와 다르게 구성됩니다.
- 외부 텍스처는 기존 GLES 텍스처 활동의 일부를 수행할 수 없습니다.
외부 텍스처의 주된 이점은 BufferQueue
데이터에서 직접 렌더링이 가능하다는 것입니다. SurfaceTexture
인스턴스는 외부 텍스처의 BufferQueue
인스턴스를 생성할 때 소비자 사용 플래그를 GRALLOC_USAGE_HW_TEXTURE
에 설정하여 버퍼의 데이터를 GLES가 인식하도록 합니다.
SurfaceTexture
인스턴스가 EGL 컨텍스트와 상호작용하므로 앱은 텍스처를 소유한 EGL 컨텍스트가 호출 스레드에 최신인 상태에서만 메서드를 호출할 수 있습니다. 자세한 내용은 SurfaceTexture
클래스 문서를 참고하세요.
타임스탬프 및 변환
SurfaceTexture
인스턴스에는 타임스탬프를 검색하는 getTimeStamp()
메서드와 변환 매트릭스를 검색하는 getTransformMatrix()
메서드가 포함됩니다. updateTexImage()
를 호출하면 타임스탬프와 변환 매트릭스가 둘 다 설정됩니다. BufferQueue
에서 전달하는 각 버퍼에는 변환 매개변수와 타임스탬프가 포함됩니다.
변환 매개변수는 효율성 면에서 유용합니다. 경우에 따라서는 소스 데이터가 소비자에게 잘못된 방향으로 되어 있을 수 있습니다. 데이터를 소비자에게 전송하기 전에 회전하는 대신 데이터를 수정하는 변환을 활용하여 데이터를 올바른 방향으로 전송하세요. 변환 매트릭스는 데이터가 사용될 때 다른 변환과 병합하여 오버헤드를 최소화할 수 있습니다.
타임스탬프는 시간에 종속되는 버퍼 소스에 유용합니다. 예를 들어 setPreviewTexture()
가 생산자 인터페이스를 카메라 출력에 연결하면 카메라의 프레임을 사용하여 동영상을 생성할 수 있습니다. 각 프레임에는 앱이 프레임을 수신한 시점이 아닌 프레임이 캡처된 시점의 프레젠테이션 타임스탬프가 있어야 합니다. 카메라 코드는 버퍼와 함께 제공된 타임스탬프를 설정하며, 결과적으로는 일련의 타임스탬프가 지닌 일관성이 개선됩니다.
우수사례: Grafika의 연속 캡처
Grafika의 연속 캡처는 기기 카메라의 프레임을 녹화하여 화면에 표시합니다.
프레임을 녹화하려면 MediaCodec 클래스의 createInputSurface()
메서드로 노출 영역을 생성한 후 노출 영역을 카메라에 전달합니다. 프레임을 표시하려면 SurfaceView
의 인스턴스를 만들고 노출 영역을 setPreviewDisplay()
에 전달합니다. 참고로, 프레임을 녹화하면서 동시에 표시하려면 추가적인 프로세스가 필요합니다.
연속 캡처 활동은 동영상이 녹화되는 동안 카메라의 동영상을 표시합니다. 이 경우 인코딩된 동영상이 메모리의 순환 버퍼에 작성됩니다. 이 버퍼는 언제든지 디스크에 저장 가능합니다.
이 흐름에는 3가지 버퍼 큐가 사용됩니다.
App
— 앱이SurfaceTexture
인스턴스를 사용하여 카메라의 프레임을 수신한 후 외부 GLES 텍스처로 변환합니다.SurfaceFlinger
— 앱이SurfaceView
인스턴스를 선언하여 프레임을 표시합니다.MediaServer
— 입력 노출 영역으로MediaCodec
인코더를 구성하여 동영상을 생성합니다.
아래 그림의 화살표는 카메라의 데이터 전파를 나타냅니다.
BufferQueue
인스턴스에서는 생산자가 청록색, 소비자가 녹색 등으로 표시됩니다.
인코딩된 H.264 동영상은 앱 프로세스에서 RAM의 순환 버퍼로 이동합니다.
사용자가 캡처 버튼을 누르면 MediaMuxer
클래스가 인코딩된 동영상을 디스크의 MP4 파일에 작성합니다.
모든 BufferQueue
인스턴스는 UI 스레드에서 GLES 연산이 수행되는 동안 앱의 단일 EGL 컨텍스트로 처리됩니다. 인코딩된 데이터 처리(순환 버퍼를 관리하여 디스크에 작성)는 별도의 스레드에서 이루어집니다.
SurfaceView
클래스를 사용하면 surfaceCreated()
콜백은 디스플레이 및 동영상 인코더를 위한 EGLContext
및 EGLSurface
인스턴스를 생성합니다. 새 프레임이 도착하면 SurfaceTexture
는 4가지 활동을 진행합니다.- 프레임을 획득합니다.
- 프레임을 GLES 텍스처로 제공합니다.
- GLES 명령어로 프레임을 렌더링합니다.
- 각
EGLSurface
인스턴스를 위한 변환 및 타임스탬프를 전달합니다.
그러면 인코더 스레드가 MediaCodec
에서 인코딩된 출력을 풀링하여 메모리에 보관합니다.
안전한 텍스처 동영상 재생
Android는 보호된 동영상 콘텐츠의 GPU 후처리를 지원합니다. 그러면 앱은 복잡한 비선형 동영상 효과(워프 등)에 GPU를 사용하고 보호된 동영상 콘텐츠를 텍스처에 매핑하여 일반 그래픽 장면(예: GLES 사용)과 가상 현실(VR)에 사용할 수 있습니다.
지원은 아래의 두 확장 프로그램을 사용하여 사용 설정됩니다.
- EGL 확장 프로그램 — (
EGL_EXT_protected_content
) 보호된 콘텐츠에서 작동 가능한 보호된 GL 컨텍스트 및 노출 영역을 지원합니다. - GLES 확장 프로그램 — (
GL_EXT_protected_textures
) 텍스처를 보호된 텍스처로 태깅하여 framebuffer 텍스처 첨부파일로 사용할 수 있도록 지원합니다.
Android는 SurfaceTexture
및 ACodec(libstagefright.so
)을 사용 설정하여 창의 노출 영역이 SurfaceFlinger
의 대기열에 등록되지 않은 경우에도 보호된 콘텐츠를 전송하며, 보호된 컨텍스트 내에서 사용할 수 있도록 보호된 동영상 노출 영역을 제공합니다. 이는 보호된 컨텍스트(ACodec으로 인증)에서 생성된 노출 영역에 보호된 소비자 비트(GRALLOC_USAGE_PROTECTED
)를 설정하는 방식으로 진행됩니다.
안전한 텍스처 동영상 재생은 OpenGL ES 환경에서의 강력한 DRM 구현을 위한 기반을 마련합니다. Widevine 수준 1과 같은 강력한 DRM 구현 없이는 다수의 콘텐츠 제공업체가 OpenGL ES 환경에서의 고가치 콘텐츠의 렌더링을 허용하지 않으며, DRM으로 보호된 콘텐츠를 VR에서 보는 등의 중요한 VR 사용 사례를 막는 결과로 이어집니다.
AOSP에는 안전한 텍스처 동영상 재생을 위한 프레임워크 코드가 포함됩니다. 드라이버 지원 여부는 OEM이 결정합니다. 기기 구현자는 EGL_EXT_protected_content
및 GL_EXT_protected_textures extensions
를 구현해야 합니다. 자체 코덱 라이브러리를 사용하여 libstagefright
를 대체하는 경우에는 /frameworks/av/media/libstagefright/SurfaceUtils.cpp
의 변경사항을 확인하세요. 이는 소비자 사용 비트에 GRALLOC_USAGE_PROTECTED
가 포함되어 있는 한, ANativeWindow
가 창 대기열에 직접 등록되지 않는 경우에도 GRALLOC_USAGE_PROTECTED
로 표시된 버퍼를 ANativeWindow
로 전송할 수 있게 해줍니다. 확장 프로그램 구현에 관한 자세한 내용은 Khronos 레지스트리(EGL_EXT_protected_content
및 GL_EXT_protected_textures
)를 참고하세요.