최근에 멀티미디어 관련 프로젝트를 진행하면서 GStreamer를 사용할 일이 생겼다. 간단하게 사용하기에는 알아야할 것들이나 개념들이 꽤나 많기 때문에 GStreamer를 사용하면서 정리한 내용들 및 추가적으로 학습한 내용들을 포스팅할 예정이다.

GStreamer

gstreamer overview
Gstreamer는 스트리밍 미디어 어플리케이션 생성을 위한 통합 미디어 프레임워크로, 수많은 모듈형식으로 구성된 멀티미디어 프레임워크이다. 주로 오디오, 비디오 스트림등 다양한 데이터 프로토콜의 흐름을 설계할 수 있도록 도와준다. 예를들어 어떤 영상 파일을 rtsp클라이언트를(ex. VLC)이용해서 접근할 수 있도록 rtp 데이터 스트림을 생성하는 rtsp서버를 간단하게 구현할 수 있게 해준다.

  • source
  • codecs
  • formats
  • fileters
  • sinks

FFmpeg와의 차이점

둘 다 멀티미디어 스트림을 처리하는 오픈소스 라이브러리로 차이점이 있다기보단 각 프레임워크가 가진 장점들이 있다. FFmpeg는 광범위한 수많은 코덱을 지원하고 다양한 기능을 제공하는 강력한 CLI도구가 있다. GStreamer는 프레임워크 아키텍처가 직관적이고 문서화가 잘되어있어 쉽게 멀티미디어 어플리케이션을 만들 수 있다는 장점이 있다. 실제로 영상 파일을 트랜스코딩을 할 일이 있을때는 FFmpeg CLI를 자주 사용하기도 한다.

개인적으로 FFmpeg는 트랜스코딩 및 각종 필터를 통한 후처리등 간단한 작업이 필요할 때 명령어 도구로 사용할 때 적합한 것 같고, 특정 미디어 어플리케이션 형태를 만들어야 할 때는 GStreamer 프레임워크가 사용하기에 더 편리한것 같다고 생각한다.

설치 방법

우분투 환경을 사용한다면 aptitude 패키지 매니저를 이용하여 설치하면 간단하다. 아래 명령어는 core 라이브러리, 각종 기본 플러그인들을 설치한다.

$ sudo apt install \
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libgstreamer-plugins-bad1.0-dev \
    gstreamer1.0-plugins-base \
    gstreamer1.0-plugins-good \
    gstreamer1.0-plugins-bad \
    gstreamer1.0-plugins-ugly \
    gstreamer1.0-libav \
    gstreamer1.0-doc \
    gstreamer1.0-tools \
    gstreamer1.0-x \
    gstreamer1.0-alsa \
    gstreamer1.0-gl \
    gstreamer1.0-gtk3 \
    gstreamer1.0-qt5 \
    gstreamer1.0-pulseaudio

잘 살펴보면 플러그인 라이브러리 이름이 base, good, bad, ugly로 되어있는걸 볼 수 있는데, GStreamer에선 아래와 같이 정의하고 있다.

  • gst-plugins-base: 기본 라이브러리를 제공하는 핵심 플러그인 모음
  • gst-plugins-good: LGPL을 준수하는 양질의 플러그인 집합
  • gst-plugins-ugly: 배포문제를 일으킬 수도 있는 좋은 플러그인들 (라이선스 등)
  • gst-plugins-bad: 더 높은 퀄리티를 요구하는 플러그인 집합 (문서, 유지보수등이 부실한 경우)
  • gst-libav: libav 래퍼 플러그인

파이프라인

GStreamer에서 가장 중요한 개념을 꼽자면 “파이프라인“이라고 말할 수 있을것 같다. 파이프라인은 GStreamer가 아니더라도 CI/CD 파이프라인등 컴퓨터 과학 분야에서 데이터 처리 단계를 설명할 때 범용적으로 사용되는 말인데, GStreamer에서도 같은 개념으로 쓰인다.

GStreamer 파이프라인은 멀티미디어 데이터를 처리하는 일련의 elements들로 구성되어있다. 각 요소는 resize, encoding, decoding, filtering등 특정 작업을 수행하는 역할을 하고 각 요소는 데이터가 입력(source)되는 부분, 출력(sink)되는 부분으로 이루어져있다. 예를들면 팩토리오라는 게임에서 컨베이어 벨트 혹은 파이프가 하는일과 정말 비슷하다.

아래 그림처럼 파이프라인은 tee라는 element를 통해 분기되기도 하고 queue라는 버퍼개념의 element로 구성되기도 한다.

파이프
컨베이어 벨트

파이프라인 구성요소

Elements

Element는 파이프라인을 구성하는 추상화된 Block형태를 정의한다. 특정 source가 들어오고, 처리되고, 출력(sink)되는 black box같은 개념이라고 생각하면 된다.

source
예를들어 위 예제에서 사용한 videotestsrc같은 데이터 스트림을 생성하는 element는 1개의 src만 가지고 있는 반면에, 1개의 스트림을 N개의 source로 demulptimex하는 demuxer같은 element는 여러개의 source를 갖는다.
demuxer

Pads

각 elements를 연결하는 srcsink를 GStreamer에서는 Pad라고 정의한다. Plug, Port같은 외부 인터페이스 개념이라고 생각하면 된다.
각 Pad는 caps negotitation라고 불리는 과정을 통해 element끼리의 연결을 만든다.

Bins

Bin은 elements의 집합인 컨테이너 단위이다. elements들 자체의 서브클래스 개념이며, 어플리케이션의 많은 복잡성을 추상화할 수 있게 한다. 이를테면, bin자체의의 상태를 변경함으로써 모든 elements의 상태를 변경한다. 또한 error, tag, EOS 메시지같은 각각이 포함하는 children으로부터의 메시징 버스를 전달하기도 한다.

파이프라인 실행 및 예제

gst-launch는 파이프라인을 CLI로 테스트해볼 수 있는 디버깅 유틸리티로, 각 elements들을 느낌표로 구분하여 사용한다.

gst-launch-1.0 videotestsrc ! autovideosink

커맨드를 실행하면 아래와 같은 colorbar 창이 뜨는것을 확인할 수 있다.

sourcesink element만 사용한 간단한 파이프라인으로, videotestsrc는 테스트용 비디오 스트림을 생성해주는 source 플러그인이다. pattern property를 변경하여 패턴을 변경할 수 있다. autovideosink는 display sink의 한 종류로 사용가능한 적합한 video sink를 찾아 자동으로 연결해준다. 예를들어 gtk 인터페이스를 통해 display하고 싶다면 sink부분을 아래와 같이 바꿔준다.

gst-launch-1.0 videotestsrc ! gtksink

위와 다르게 출력하는 영상은 같지만 gtk 인터페이스로 창을 생성한것을 볼 수 있다.

audio 소스 추가하기

videotestsrc처럼 오디오도 테스트용 source element가 있다 (audiotestsrc). source를 추가할 때는 느낌표 구분자를 사용하면 구문 오류가 나니 주의한다.

gst-launch-1.0 \
    videotestsrc ! autovideosink \
    audiotestsrc ! autoaudiosink

위 명령어를 실행하면, 기존의 colorbar창에 삐 소리가 나는것을 확인할 수 있다.

Communication

어플리케이션과 파이프라인 사이의 데이터 교환및 커뮤니케이션을 위한 메커니즘을 제공한다.

  • buffers: 파이프라인의 element들 사이의 스트리밍 데이터를 나아가게하는 객체이다. buffers는 항상 source → sink 방향으로 이동한다. (upstream → downstream)
  • events: elements혹은 어플리케이션에서 elemets 사이의 전달된 객체이다. events는 upstream → downstream 방향으로 이동하고, downstream event는 데이터 흐름에 동기화될 수 있다.
  • messageses: 파이프라인 메시징 버스에 의해 posted된 객체이다. 스트리밍 스레드 컨텍스트로부터 동기적으로 가로채질 수 있다. 하지만 주로 어플리케이션의 메인스레드로부터 어플리케이션에 의해 비동기적으로 다루어진다. error, tag, state change, buffering, redirecte 같은 정보를 전송하는데 사용된다.
  • querires: 구간,재생위치같은 정보를 요청한다. 쿼리는 항상 동기적으로 응답된다. peer elements로부터 정보를 요청하기 위해 queries를 사용할 수도 있다. (file size 나 duration같은), 파이프라인내에서 둘 다 사용될 수 있지만, 보통은 upstream quires가 일반적이다.

References