GStreamer는 미디어 스트리밍 어플리케이션 작성을 위한 오픈 소스 프레임웍으로서 단위 기능 블럭을 파이프라인(pipeline) 처럼 조립해서 어플리케이션을 만들 수 있게 해준다. 본 글에서는 기본적인 용어를 먼저 설명한 후 GStreamer에 내장된 명령어 기반 유틸리티인 gst-launch를 사용하여 RTP 스트리밍을 발생하는 법을 다양한 예를 통해 제시한다.
용어
엘리먼트(Element) GStreamer의 구체적인 단위 기능을 담당하는 요소를 엘리먼트라고 하며, 이러한 엘리먼트들을 연결해서 체인 또는 파이프라인을 구축한 후 데이터를 흐르게 한다. 패드(Pad) 엘리먼트의 입/출력(In/Out)을 패드라고 부른다. 엘리먼트를 연결한다는 의미는 실질적으로는 패드를 연결하다는 말과 같다. 구체적으로는 엘리먼트간 연결에 앞서 협상을 하고, 협상 후 실제 데이타를 흐르게 하는 역할을 담당한다. 패드별로 데이타를 다룰 수 있는 능력(Capabilities)이 다르기 때문에 아무 패드끼리 연결할 수는 없다. 두 패드간에 데이타 타입(GstCaps)을 협상(caps negotiation)해서 성립하는 경우만 연결이 된다.
데이타를 내보내는 역할을 하는 것을 소스 패드(source pad)라고 하고 반대로 데이타를 받는 역할을 하는 것을 싱크 패드(sink pad) 라고 부른다. 데이타는 구체적으로는 버퍼와 이벤트를 지칭한다.
빈(Bin) 엘리먼트를 여러개 모아 놓은 컨테이너를 빈이라고 하는데 기본적으로는 엘리먼트와 유사하게 동작한다. 빈을 사용하는 목적은 설계를 단순하게 유지하기 위함이다. 가령, 세세하고 복잡한 부분은 엘리먼트에서 구현하고, 빈은 추상화를 담당하게 하는 식이다. 빈의 상태(state) 변화는 그 빈 내부의 모든 엘리먼트의 상태 변화를 발생시키는 특징을 가지고 있다. 다른 중요한 기능으로는 빈이 포함하고 있는 내부 엘리먼트들의 메시지(에러, Tag, EOS)를 버스를 통해서 상위 어플리케이션으로 전달(forward)하는 기능등이 있다. 파이프라인(Pipeline) 최상위 빈을 파이프라인이라고 한다. 파이프라인은 상위에 있는 어플리케이션과 자신의 자식들(빈, 엘리먼트)간 소통을 위해 버스를 제공한다. 버퍼(Buffer) 엘리먼트간 데이타를 주고 받는데 사용하는 오브젝트이다. 다운스트림(소스에서 싱크)으로만 흐름이 존재한다. 이벤트(Event) 두 가지 형태의 이벤트가 있다. 첫번째는 어플리케이션에서 엘리먼트로 정보(Play, Pause 등)를 보내기 위해 사용하는 경우이고, 다른 형태는 엘리먼트 상호간에 정보 교환용이다. 엘리먼트간에 교환되는 이벤트는 업스트림과 다운스트림 방향으로 모두 사용이 가능하다. 메시지(Message) 메시지는 엘리먼트에서 발생하며, 파이프라인에서 만든 메시지 버스를 통과해서 어플리케이션로 전달되는 구조를 갖는다. 메시지는 errors, tags, state changes, buffering state, redirects 과 같으 정보를 전달하는 목적으로 사용된다. 퀴리(Query) 어플리케이션에서 엘리먼트에 정보를 요청하고, 엘리먼트는 응답하는 경우에 사용되는 오브젝트이다. 에를 들면 어플리케이션에서는 전체 시간(duration), 경과 시간, 플레이백 정보를 특정 엘리먼트에 질의(Query)할 수 있다. 쿼리는 엘리먼트간에도 사용되는데 가령 다운스트임에 있는 엘리먼트가 상위에 놓인 엘리먼테에게 미디어 사이즈나 전체 시간등의 질의를 할 수 있는 식이다.
비디오 소스와 싱크 엘리먼트간 직접 연결
gst-launch videotestsrc ! autovideosink
gst-launch autovideosrc ! autovideosink
gst-launch autovideosrc ! osxvideosink
capsfilter를 사용한 출력 화면 크기 변경
gst-launch autovideosrc ! videoscale ! capsfilter caps="video/x-raw, width=640, height=480" ! osxvideosink
JPEG 인코딩/디코딩 엘리먼트
gst-launch autovideosrc ! jpegenc ! jpegdec ! autovideosink
JPEG과 RTP 스트리밍
송신 gst-launch autovideosrc ! jpegenc ! rtpjpegpay ! udpsink host=127.0.0.1 port=5000 수신 gst-launch udpsrc port=5000 caps="application/x-rtp, encoding-name=(string)JPEG, payload=(int)26" ! rtpjpegdepay ! jpegdec ! autovideosink
H.264 인코딩/디코딩 엘리먼트
gst-launch autovideosrc ! x264enc ! h264parse ! avdec_h264 ! autovideosink
H.264와 RTP 스트리밍
송신 gst-launch autovideosrc ! x264enc ! h264parse ! rtph264pay ! udpsink host=127.0.0.1 port=5000 수신 gst-launch udpsrc port=5000 caps="application/x-rtp, encoding-name=H264, payload=96" ! rtph264depay ! h264parse ! avdec_h264 ! autovideosink
H.264 지연 개선
gst-launch autovideosrc ! x264enc tune=zerolatency ! h264parse ! avdec_h264 ! autovideosink
gst-launch autovideosrc ! x264enc tune=zerolatency byte-stream=true bitrate=3000 threads=2 !
h264parse ! avdec_h264 ! autovideosink
지연 개선된 H.264와 RTP 스트리밍
송신 gst-launch autovideosrc ! x264enc tune=zerolatency byte-stream=true bitrate=3000 threads=2 ! h264parse config-interval=1 ! rtph264pay ! udpsink host=127.0.0.1 port=5000 수신 gst-launch udpsrc port=5000 caps="application/x-rtp, encoding-name=H264, payload=96" ! rtph264depay ! h264parse ! avdec_h264 ! autovideosink
시간 정보 오버레이
gst-launch autovideosrc ! timeoverlay ! osxvideosink
시간 정보 오버레이 시킨 후 로컬과 원격 비교
송신 gst-launch autovideosrc ! timeoverlay ! tee name="local" ! queue ! autovideosink local. ! queue ! x264enc tune=zerolatency byte-stream=true bitrate=3000 threads=2 ! h264parse config-interval=1 ! rtph264pay ! udpsink host=127.0.0.1 port=5000 수신 gst-launch udpsrc port=5000 caps="application/x-rtp, encoding-name=H264, payload=96" ! rtph264depay ! h264parse ! avdec_h264 ! autovideosink
VP8 인코딩/디코딩 엘리먼트
gst-launch autovideosrc ! videoconvert ! vp8enc ! rtpvp8pay ! rtpvp8depay ! vp8dec ! autovideosink
VP8와 RTP 스트리밍
송신 gst-launch autovideosrc ! videoconvert ! vp8enc target-bitrate=3000 threads=2 ! rtpvp8pay ! udpsink host=127.0.0.1 port=5000 수신 gst-launch udpsrc port=5000 caps="application/x-rtp, media=(string)video, encoding-name=(string)VP8, payload=(int)96, clock-rate=(int)90000" ! rtpvp8depay ! vp8dec ! autovideosink
두 머신(Machine)간 H.264-RTP 시험: GStreamer 송신 / VLC 수신
송신 (e.g., 192.168.1.100) gst-launch autovideosrc ! x264enc ! h264parse ! rtph264pay ! udpsink host=192.168.1.97 port=5000 수신 (e.g., 192.168.1.97) /Applications/VLC.app/Contents/MacOS/VLC h264.sdp

# h264.sdp
v=0
m=video 5000 RTP/AVP 96
c=IN IP4 127.0.0.1
a=rtpmap:96 H264/90000
두 머신(Machine)간 VP8-RTP 시험: GStreamer 송신 / OpenCV 수신
송신 (e.g., 192.168.1.100) gst-launch autovideosrc ! videoconvert ! vp8enc target-bitrate=3000 threads=2 ! rtpvp8pay ! udpsink host=192.168.1.97 port=5000 수신 (e.g., 192.168.1.97)

VideoCapture video;
// video.open("./h264.sdp");
video.open("./vp8.sdp")

# vp8.sdp
v=0
m=video 5000 RTP/AVP 96
c=IN IP4 127.0.0.1
a=rtpmap:96 VP8/90000
gst-inspect 사용법
gst-inspect | grep -i video | grep -i sink
gst-inspect-1.0 x264enc