Audio/Video capture기술

안녕하세요, Mobile Platform개발팀 김종일입니다.
저는 이번 포스팅을 통해 사내 Play4Me Project를 진행하면서 검토하고 적용한 Audio/Video capture기술에 대해 설명하려고 합니다.  Play4Me는 OpenGL 기반의 게임 엔진에서 생성되는 Audio/Video를 capture하여 이를 저장한 video 파일을 만들거나 서버를 통하여 online streaming을 할 수 있는 기술이 사용되었는데요~ 이 기술로 할 수 있는 간단한 데모 영상 하나 먼저 보시겠습니다.

이 프로젝트를 진행하면서 Video capture, Audio capture의 기능 검토를 진행하여 겪었던 어려움과, 이를 해결했던 내용들에 대해 말씀 드리겠습니다.

게임 엔진 모르게 이미지 가져오기 – FBO

일반적으로 게임 엔진들은 Render Loop을 만들고 Render Loop에서 화면에 그리기 위한 작업을 진행하게 됩니다.
OpenGL에서는 framebuffer를 이용하여 이미지들을 완성하고, framebuffer의 내용을 LCD로 출력을 하는 방식으로 디스플레이 하게 됩니다.
게임 엔진에서 생성된 이미지를 얻어내기 위해 가장 쉬운 방법은 게임 엔진의 인터페이스를 만들어 이미지를 받으면 될 텐데, 엔진을 수정할 수 없다는 제약사항에 많은 고민을 하게 되었습니다.
고민 끝에 OpenGL이 state machine형태로 동작을 한다는 점에 착안하여 OpenGL의 FBO를 이용하기로 결정하였습니다.
게임 엔진이 화면에 디스플레이를 위한 state설정을 모두 마친 지점에서 임의의 FBO를 설정하게 되면, 게임 엔진은 자신이 설정한 framebuffer에 draw를 한다고 가정하고 이미지를 그리게 되는데, 실제로는 저희가 설정한 FBO에 그리게 되는 것입니다.
Render loop이 종료된 시점에서 설정한 FBO에 저장된 이미지를 빼낸 뒤, 게임 엔진이 애초에 설정했던 framebuffer에 다시 그려주게 되면 게임 엔진의 수정 없이 그려진 이미지를 프레임 단위로 얻어낼 수 있습니다.

GPU에서 CPU로 image 빠르게 가져오기 – Texture Cache

FBO에 담겨진 이미지 데이터는 GPU에서 관리가 되고 있기 때문에, 이미지의 인코딩을 위해선 CPU로 데이터를 옮겨와야 합니다.
OpenGL에서는 픽셀 데이터의 read를 위하여 glReadPixel() API를 제공하고는 있습니다만,
GPU와 CPU간의 데이터 복사에 많은 시간이 소요되므로 real-time처리를 위해서는 적합하지 않는 방법입니다.
저희가 작업했던 iPhone5상황에서는 1136 x 640 resolution의 이미지를 초당 30회 복사를 해야 하므로 더더욱 다른 방법이 필요했습니다.
여러 검색 끝에 iOS에서는 GPU에서 관리되는 메모리에 빠르게 접근하기 위한 방법으로 Texture Cache라는 것을 제공하고 있다는 것을 알게 되었습니다.
Texture Cache라는 것은 GPU상에서 관리되는 Texture의 메모리에 CPU에서 직접 접근할 수 있도록 인터페이스를 제공하고 있는 것인데요,
앞서 고민했던 FBO를 생성할 때 Texture Cache형태의 Texture를 FBO에 bind한 뒤 원하는 시점에 Texture cache에 접근을 하게 되면 이미지를 빠르게 얻을 수 있었습니다.
물론 OpenGL에서 관리되는 좌표계와, OS에서 관리되고 있는 좌표계가 y축으로 뒤집어진 형태이므로 Shader program을 이용하여 GPU 상에서 OS의 좌표계와 맞춰 y축으로 이미지를 뒤집은 상태에서 Texture Cache로 접근을 하여야 정상적인 이미지를 얻을 수 있습니다.

Audio Data 가져오기 – CoreAudio

이번 프로젝트에서 제일 까다로웠던 부분이 바로 Audio를 capture하는 부분이었습니다.
Audio는 iOS의 framework에서 Audio Unit이라는 형태로 직접 관리하고, capture 를 위한 API를 제공하고 있지 않았기 때문에 많은 검토와 시도가 필요했습니다.

첫 번째로 시도를 해 본 내용은 method swizzling 입니다.
method swizzling은 Objective-C로 작성된 instance의 method를 임의의 method로 교체시켜 원하는 API를 동작시키도록 하는 방법입니다.
그런데 이 방법은 App store등록 심사 시 reject의 사유가 되며, Audio를 관장하는 Core Audio framework에서는 C형태의 API로 기능들을 제공하고 있어 사용할 수가 없었습니다.
그래서 iOS가 OSX 기반으로 작업이 되었다는 점에 착안하여, OSX상에서 Audio의 동작형태를 살펴보았습니다.
Apple에서 제공하고 있는 Core Audio Utility Class 를 참고하여 OSX에서 Audio Unit이 어떻게 동작하는지 알 수 있었습니다.
이를 살펴보면 C API형태로 제공되는 내용들도 모두 구현은 C++형태로 구현이 되어 있다는 사실을 알 수 있습니다.
Audio Unit을 살펴보면 각 Unit을 관리하기 위한 정보들이 있는데, 이것들이 결국 C++ instance임을 알 수 있고, C형태의 API는 Singletone으로 제작된 Class를 이용한다는 것을 유추해 낼 수 있습니다.
Core Audio의 API들의 동작을 ASM level로 확인해 본 뒤 이를 확신할 수 있었습니다.
결국, Audio Unit을 사용하기 위해 관리되는 instance들은 Singletone class에서 모두 관리가 되므로, 이 class에 접근하면 게임 엔진이 사용하는 Audio Unit의 instance를 알아낼 수 있고, 이를 이용하여 현재 rendering되고 있는 Audio data를 PCM형태로 추출해 낼 수 있게 되었습니다.


맺음말

위의 검토를 iOS기반으로 진행을 하면서 느낀 소감 몇 가지를 마지막으로 덧붙이자면..
1. iOS의 개발환경이 생각보다 잘 되어 있음을 느낄 수 있었습니다.
2. 많은 연산은 되도록 CPU가 아닌 GPU, DSP로 넘기는 것이 좋습니다. iOS에서는 DSP를 활용할 수 있는 vDSP API도 제공하고 있습니다. 역시 멋진 환경입니다.
3. iOS에는 공개되지 않은 내용들이 많아 답답한 경우가 있는데, 이런 경우 OSX의 환경을 참고하시면 좋습니다. OSX와 iOS가 유사한 점이 많기 때문에 공개되어 있는 OSX의 샘플 코드 등을 활용하면 큰 도움이 됩니다.

관련하여 궁금한 사항은 언제든지 댓글로 연락해 주시면 말씀 드리도록 하겠습니다.

감사합니다.

김종일 Mobile Platform개발팀

Mobile Platform개발팀에서 Android/iOS기반의 Media data 처리와 관련된 프로젝트들을 진행하고 있습니다.

공유하기