GStreamer User Book #1

본 연재는 SK Telecom의 후원으로 진행하는 “책책책 책을 만듭시다!” 프로그램으로 기획되었으며, 연재 종료 후 도서로 출간될 예정입니다. 연재 내용 중에 저자의 주관적 의견이 노출되는 경우, 해당 의견은 SK Telecom의 의견이 아님을 미리 밝힙니다.

GStreamer 소개

GStreamer는 미디어 정보를 처리하기 위한 소프트웨어 집합, 즉 미디어프레임워크이며, 가장 신사적인 오픈 소스 프로젝트 커뮤니티이기도 하다. 미디어 프레임워크로서 GStreamer는 사용자 애플리케이션이 영상, 음성 및 데이터를 제한된 컴퓨팅 자원과 주어진 시간 내에서 처리할 수 있도록 도와주는 역할을 수행한다. 이미 개발된 표준안과 기술들에 대해서는 소프트웨어적인 신뢰성을 제공하고,새로운 표준과 기술들에 대해서는 빠르게 적용하고 최적화할 수 있는 기본 토대를 마련해주는 것을 목표로 하고 있다.

GStreamer의 시작은 매우 단순지만, 소프트웨어 프레임워크들이 해결하고자 하는 공통적인 문제인 코드의 재 사용성과 추상화된 계층 구조에 대한 요구 사항으로 부터 출발한다. 1999년 이전의 상황으로 잠시 돌아가서 생각해본다면, MP3 파일을 재생하는 사용자 애플리케이션 작성을 위해서는 MP3 디코더부터 구현해야만 했다. 성공적으로 MP3 재생기를 구현하였다고 하더라도, 다른 애플리케이션으로 확장을 하기 위하여 디코더 부분만을 재사용한다거나, 새로운 코덱을 지원하도록 기능을 추가한다던가 하는 작업은 온전히 소프트웨어를 구현하는 엔지니어의 역량에 의존할 수 밖에 없는 상황이었다.

그 당시에 사용할 수 있는, 재사용이 가능한 디코더 라이브러리, 혹은 미디어 재생기와 같은 프로그램은 1996년부터 사용가능 했던 Microsoft의 DirectShow나 그 이전에는 1991년부터 Apple이 공급한 QuickTime 정도에 지나지 않았으며, 특히 리눅스와 같은 환경에서 사용가능한 미디어 정보 처리를 위한 소프트웨어 라이브러리 혹은 애플리케이션은 전무한 상태였다. 당시의 설계자들이 해결하고자 했던 또다른 문제는 단순한 기능적인 요구사항뿐 아니라, 지속적인 발전이 가능한 라이센스 모델을 찾는 것이었다. VLC의 경우 GPL을 사용하여 프로젝트 공헌자들의 아이디어를 다시 오픈 소스 커뮤니티로 강제로 환원하는 정책을 선택하였으며, GStreamer는 그와 달리, 산업에서 사용할 때 보다 자유로운 LGPL을 채택함으로써 임베디드 시스템에서도 사용이 가능한 미디어프레임워크로 발전하게 되었다.

미디어프레임워크 주요 릴리즈 연도

GStreamer 구성 요소

GStreamer를 간단히 정의한다면, 플러그인들로 구성된 모듈의 집합이라고 할 수 있다. 일반적으로 인식하고 있는 모듈과 플러그인의 정의와 약간 다를 수는 있으나, GStreamer 커뮤니티에서는 모듈을 소스 코드 저장소로 정의하고 있고, 각 저장소는 기능을 모아둔 라이브러리와 플러그인으로 구성되어있다. 기능을 정의하기 위한 단위는 Element이며 하나의 기능을 구현하기 위한 기본 단위는 Element와 Pad이다. 각 Element는 다수의 Pad를 생성할 수 있고, Pad의 속성에 따라서 Element를 연결할 수 있는지가 결정된다.

각 Element를 연결하여 정보의 흐름을 제어하는 아이디어는 DirectShow에서 차용하였으나, GStreamer 프로젝트는 C 언어 기반으로 작성된 완전히 다른 미디어 프레임워크이다. 조금 더 덧붙이자면 형식적으로는 C 언어 기반으로 작성되었다고 말할 수 있으나, 정작 내부 구조는 너무나 철저하게도 객체지향적이기 때문에, 명확한 정의는 아니라고 할 수 있다. 이는 GStreamer의 기본 단위가 GObject에서 파생되었기 때문이며, 보충 설명을 위하여 연재의 한 챕터를 GObject를 설명하는데 할애하려고 한다.

GstObject 클래스 계층 구조

GStreamer 모듈

플러그인으로 나뉘어진 각 기능들을 전략적으로 관리하기 위하여 GStreamer는 다수의 모듈, 즉 저장소를 관리하고 있다. 사실 GStreamer SDK를 이용하여 애플리케이션을 작성한다고 하면 모듈 내부의 소스 코드들을 직접 분석할 필요는 없다. 그러나 단위 기능이나 Element에 대한 테스트와 예제를 tests 디렉토리에서 찾을 수 있기 때문에 코드 작성중에 올바른 사용 방법 혹은 불확실성에 대한 확신을 얻고자 한다면 SDK를 사용한다고 하더라도 저장소 코드는 직접 살펴보는 것이 검색 엔진을 통한 질의 보다 효율적이다.

gstreamer

커뮤니티에서는 core 라고 부르기도 하며, GStreamer를 구동하기 위한 필수 도구와 라이브러리를 제공한다. 특히 gst-launch, gst-inspect 처럼, CLI로 제공되는 도구들은 GStreamer의 기본적인 동작과 디버깅 기법을 이해하는데 매우 큰 도움이 된다.

gst-plugins-base

이름에서 유추할 수 있는 것처럼, 기본 라이브러리와 플러그인을 제공한다. 기능들에 대한 동작성이 철저하게 검토된 플러그인들을 관리하는 공간이며, base 모듈 내에서 버그 혹은 다른 어떠한 이슈가 발견된다면, 매우 즉각적으로 대응을 하고 있다. OpenGL을 통한 렌더링, CPU 기반의 오디오 및 비디오 처리 기능을 담당하는 Element들을 포함하고 있으며 gst-play 와 같은 High-Level Playback 기능을 사용할 수 있는 CLI를 제공하는 공간이기도 하다.

gst-plugins-good

사용자 애플리케이션을 작성하는 경우, 보통은 core, base, good 이 세 모듈을 기반으로 작성할 수 있다. 특히 임베디드 시스템에 탑재하는 경우 발생할 수 있는 라이센스의 충돌이나 의도치 않는 정책들에 대해서 비교적 자유로운 코드들이 good 모듈에 탑재된다. 기능 상으로도 누구나 좋다고 인정하는 수준의 동작성, 코드 상의 무결성 등이 검토된 라이브러리와 플러그인들이 위치한다. RTSP와 관련된 Element들이 대표적이며, V4L2 (Video for Linux) API를 이용한 기능들도 이곳에 있다.

gst-plugins-ugly

이제 이 모듈부터는 상용 소프트웨어를 제작하는데 주의가 필요하다고 보면 되는 공간이다. 모듈의 이름이 ugly이지만, 소스 코드의 품질이나 기능 동작 상태에 관련된 명칭은 아니며, 대체적으로 기능적으로는 잘 동작하지만 라이센스에 대해서 검토가 필요할 수도 있다는 정도의 의미로 해석하는 것이 좋다. RealMedia나 ASF에 대한 지원이 대표적인 기능이며, 다른 모듈과 비교하여 활동량이 적은 공간이다.

gst-plugins-bad

GStreamer 컨트리뷰터가 되고 싶지만 시작점을 찾기 어렵다거나, 새로운 플러그인을 제안하고 제작하는 것을 고려하고 있다거나, 최신 동향을 분석하기 위해서는 절대적으로 익숙해져야하는 모듈이 bad이다. ugly라는 모듈명을 정의할때 사용한 뉘앙스처럼, bad 또한 무언가 나쁜것이 있는 공간이다라고 해석해서는 안된다. 기능적으로 동작하고 검증되었지만 누군가 더 사용해서 유즈 케이스를 확보하고 싶다거나, 자신이 설계한 기능에 대한 여러 사용자들의 동의를 얻기 위한다던가, 아니면 라이센스에 대해서 검토가 이루어지지 않은 코드들에 대한 공간으로 인식하는 것이 바람직하다. 그렇기 때문에 새롭게 제작한 플러그인이 있다면 이 모듈을 타겟으로 제출하면 된다. 대표적으로 안드로이드와 iOS를 지원하는 라이브러리와 Element가 이 공간에 위치한다.

기타 저장소

core, base, good, ugly, bad는 GStreamer를 구성하는 모듈이자 저장소이지만, GStreamer 프로젝트는 이외에도 다수의 저장소로 구성되어 있다. 특정한 프로그래밍 언어를 지원하기 위한 바인딩 프로젝트와 SDK 빌드를 위한 저장소, 그리고 레퍼런스로 사용할 수 있는 예제들이 별도의 저장소에서 운영되고 있다.

  • 프로그래밍 언어 바인딩 - gst-python, gstreamer-rs, gstreamer-sharp
  • 코덱과 필터 - gst-libav
  • RTSP 서버 - gst-rtsp-server
  • 레퍼런스 애플리케이션 - gst-examples
  • SDK 빌드 - cerbero
  • GStreamer 모듈 빌드 - gst-build

GStreamer 아키텍쳐

벌써 GStreamer에 대한 필수 구성 요소들에 대한 이야기들을 많이 했지만, 정작 그들간의 관계와 어떻게 미디어 정보를 처리할 수 있는지에 대한 이야기는 아직 하지 못했다. GStreamer 아키텍쳐를 계층별 흐름대로 설명하자면, 오브젝트 단위로 구성된 (Element로 구성된) 기능들을 플러그인으로 만들고, 그 플러그인들을 런타임에 동적으로 사용할 수 있도록 하는 기능을 가진 core 프레임워크 위에 애플리케이션을 만들 수 있다.

앞서 잠깐 언급한 CLI 도구들도 GStreamer를 사용한 애플리케이션이며, 다른 형태의 미디어 처리기를 제작할 때에도 GStreamer 프레임워크를 사용할 수 있다. 다만 어떠한 플러그인을 사용할지, 어떠한 Element를 사용할지는 전적으로 GStreamer core가 담당하게 된다. 좀더 프로그래머의 언어로 설명을 하자면, 헤더 파일을 통해서 접근하고 링크를 만들 수 있는 함수는 core까지이며, 각각의 플러그인이나 Element에는 함수 단위의 접근이 불가능하다. 뒤에서 좀더 자세하게 다루겠지만, Element 단위의 접근을 하기 위해서는 Property나 Singal이 유일한 경로이며, 이 또한 Element가 명시적으로 제공하지 않는한 애플리케이션 계층에서 Element를 직접적으로 제어할 수 있는 방법은 없다. 또한 core는 가장 중요한 개념인 Pipeline을 관리할 수 있는 기능을 포함하며, 애플리케이션과 Pipeline 간의 대화가 가능한 채널인 Bus를 제공한다. 즉, core는 각 플러그인과 Element에 대한 접근을 은폐하고 추상화된 제어함수를 제공하게 되는 것이다.

GStreamer 아키텍쳐

플러그인 계층은 특수한 형태로 제작된 라이브러리의 집합이다. GStreamer는 기본적으로 런타임에 관리가 가능한 동적 로딩 방식으로 플러그인을 관리하고 있으며, 이 플러그인들은 파일 시스템 기준으로 본다면 특수하게 제작된 동적 라이브러리들이다. 그렇기 때문에 제작된 플러그인은 반드시 소스코드로 배포될 필요는 없고, 플러그인 제작자가 의도한다면 바이너리 형태의 배포가 가능하다. 이러한 플러그인 로딩 방식은 지적 재산권을 보호하기 위한 방편으로도 사용할 수 있다. 실제로 많은 SoC 벤더들이 하드웨어 코덱이나 보안을 강화한 플러그인을 배포할때 바이너리 배포 방식을 사용하고 있다.