[GStreamer] Push, Pull 방식

GStreamer 2017.04.05 16:08 Posted by 김한별 behonestar


이 글은 GStreamer 튜토리얼 문서 Different scheduling modes를 요약한 글입니다.


엘리먼트의 Pad는 Push 방식과 Pull 방식의 스케쥴링 모드를 지원합니다. 대표적으로 아래와 같은 use-case가 존재합니다.


1. 모든 엘리먼트의 패드들이 push 모드인 경우

① 첫번째 엘리먼트는 다음 엘리먼트의 sinkpad에게 버퍼를 push하는 작업을 반복합니다.

② 두번째 엘리먼트는 sinkpad에 등록된 _chain() 함수를 통해 전달받은 버퍼를 처리합니다.

③ _chain() 함수는 gst_pad_push() 함수를 통해 다음 엘리먼트의 sinkpad에게 버퍼를 push합니다.



2. sinkpad는 pull 모드, srcpad는 push 모드인 경우

① sinkpad가 pull 모드로 활성화되면 사용자가 정의한 GstTask 스레드가 시작됩니다.

② 해당 스레드는 gst_pad_pull_range() 함수를 통해 이전 엘리먼트의 버퍼로부터 특정 범위의 데이터를 얻어와서 데이터를 처리합니다. sinkpad가 데이터의 흐름을 제어하게 되는 것이죠.

③ gst_pad_push() 함수를 통해 다음 엘리먼트의 sinkpad에게 버퍼를 push합니다.


* sinkpad를 pull 모드로 설정하는 방법

엘리먼트의 상태가 READY -> PAUSED로 변경될 때 패드가 활성화됩니다. 내부적으로는 _activate() 함수와 _activate_mode() 함수가 호출됩니다. pull 모드로 변경하려면 이 두가지 함수를 오버라이딩하여 스케쥴링 모드를 변경하면 됩니다. 오버라이딩하지 않으면 기본값인 push 모드로 동작합니다.


1) _activate() 함수 오버라이딩 

static void

gst_my_filter_init (GstMyFilter * filter)

{

  [...]

  gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);

  [...]

}


2) 이전 엘리먼트의 srcpad가 pull 모드를 지원하면, 현재 엘리먼트의 sinkpad도 pull 모드로 설정

static gboolean

gst_my_filter_activate (GstPad * pad, GstObject * parent)

{

  GstQuery *query;

  gboolean pull_mode;


  query = gst_query_new_scheduling ();


  if (gst_pad_peer_query (pad, query)) {

    pull_mode = gst_query_has_scheduling_mode_with_flags (query,

        GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);


    if (pull_mode) {

      gst_query_unref (query);

      return gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE);

    }

  }


  gst_query_unref (query);

  return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);

}


3) _activate_mode() 함수 오버라이딩

static void

gst_my_filter_init (GstMyFilter * filter)

{

  [...]

  gst_pad_set_activatemode_function (filter->sinkpad, gst_my_filter_activate_mode);

  [...]

}


4) pad가 pull 모드로 활성화되면 GstTask 스레드 실행, 비활성화되면 GstTask 스레드 종료

static gboolean

gst_my_filter_activate_mode (GstPad    * pad,

                 GstObject * parent,

                 GstPadMode  mode,

                 gboolean    active)

{

  gboolean res;

  GstMyFilter *filter = GST_MY_FILTER (parent);


  switch (mode) {

    case GST_PAD_MODE_PUSH:

      res = TRUE;

      break;

    case GST_PAD_MODE_PULL:

      if (active) {

        filter->offset = 0;

        res = gst_pad_start_task (pad,

            (GstTaskFunction) gst_my_filter_loop, filter, NULL);

      } else {

        res = gst_pad_stop_task (pad);

      }

      break;

    default:

      /* unknown scheduling mode */

      res = FALSE;

      break;

  }

  return res;

}


5) GstTask 스레드 구현 : 이전 엘리먼트로부터 데이터를 읽어와서 다음 엘리먼트에게 push 

    static void

    gst_my_filter_loop (GstMyFilter * filter)

    {

      GstFlowReturn ret;

      guint64 len;

      GstFormat fmt = GST_FORMAT_BYTES;

      GstBuffer *buf = NULL;


      [...]


      /* now, read BLOCKSIZE bytes from byte offset filter->offset */

      ret = gst_pad_pull_range (filter->sinkpad, filter->offset,

          BLOCKSIZE, &buf);


      [...]


      /* now push buffer downstream */

      ret = gst_pad_push (filter->srcpad, buf);


      [...]

    }



3. 현재 엘리먼트의 모든 패드들이 pull 모드인 경우

srcpad가 pull 모드로 동작한다는 것은 다음 엘리먼트의 sinkpad가 gst_pad_pull_range()를 통해 데이터를 가져갈 수 있도록 지원하겠다는 것을 의미합니다.


sinkpad가 pull 모드로 동작한다는 것은 gst_pad_pull_range()를 통해 이전 엘리먼트로부터 데이터를 가져오겠다는 것을 의미합니다. 


그런데 sinkpad와 srcpad가 모두 pull 모드이면 엘리먼트는 다음 엘리먼트에 종속되어 버립니다. 다음 엘리먼트가 gst_pad_pull_range()를 호출하면, 현재 엘리먼트도 gst_pad_pull_range()를 호출하여 이전 엘리먼트로부터 데이터를 가져와야 합니다. 따라서 sinkpad를 pull 모드로 설정하되 GstTask 스레드는 실행되지 않도록 합니다.


* srcpad를 pull 모드로 설정하는 방법

다음 엘리먼트의 sinkpad가 gst_pad_pull_range()를 통해 데이터를 가져갈 수 있도록 _get_range() 함수를 등록해줍니다. 


1) _get_range() 함수 오버라이딩

static void

gst_my_filter_init (GstMyFilter * filter)

{

  [...]

  gst_pad_set_getrange_function (filter->srcpad, gst_my_filter_get_range);

  [...]

}


2) 이전 엘리먼트로부터 데이터를 읽어와서 outbuf에 담고 데이터 처리

static GstFlowReturn

gst_my_filter_get_range (GstPad     * pad,

             GstObject  * parent,

             guint64      offset,

             guint        length,

             GstBuffer ** outbuf)

{


  GstMyFilter *filter = GST_MY_FILTER (parent);

  [...]

  /* pull data */

  gst_pad_pull_range (filter->sinkpad, filter->offset, BLOCKSIZE, outbuf);
  

  /* process data */
  [...]


  return GST_FLOW_OK;

}


끝.

[GStreamer] RTSP의 H264 영상 Dump 방법

GStreamer 2017.03.29 10:03 Posted by 김한별 behonestar

RTSP로 스트림되는 H264 영상을 GStreamer로 Dump하는 방법입니다. rtph264depay를 통해 출력되는 h264 데이터의 stream-format을 byte-stream으로 지정해줘야 streameye와 같은 툴로 분석이 가능합니다.


gst-launch-1.0 rtspsrc location="rtsp://media.smart-streaming.com/mytest/mp4:sample.mp4" ! rtph264depay ! video/x-h264,stream-format=byte-stream ! h264parse ! filesink location="/path/to/video.h264"



*H264 Byte Stream 포맷?

- SPS > PPS > I/P/B 슬라이스 순서로 나열됩니다.

- 각 프레임은 SCP(0x000001 또는 0x00000001)로 시작합니다.

- SCP와 SCP 사이에는 완전한 프레임 한장이 들어갑니다.

- 각 Access Unit 앞에 AUD(Access Unit Delimiter)를 넣어서 Access Unit의 시작을 표현할 수 있습니다.

 (※ HTML5 video 태그로 h264 영상을 재생할 때 AUD가 없는 영상은 재생이 안되더라구요!)




*H264 Access Unit?

- MPEG 규격의 비디오와 오디오 데이터를 다룰 때 기본 단위입니다.

- Access Unit 하나의 구조는 아래와 같습니다.

- Access Unit Delimiter는 00 00 00 01 09 xx 포맷을 따릅니다. (I: 0x10, P: 0x30)



[GStreamer] 윈도우 Visual Studio 개발 환경 설정

GStreamer 2017.03.06 19:42 Posted by 김한별 behonestar

Windows7 64bit, Visual Studio 10 기준으로 기술하였습니다.



GStreamer 1.10.4 설치

1. 다운로드 

2. gstreamer-1.0-x86_64-1.10.4.msi / gstreamer-1.0-devel-x86_64-1.10.4.msi 설치

 ※ 설치 경로를 기억하세요. 저는 D:\gstreamer에 설치하였습니다.



환경변수 등록

1. 내 컴퓨터 > 속성 > 고급시스템 설정 > 환경변수 > 시스템 변수 > Path [편집]

2. ..;D:\gstreamer\1.0\x86_64\bin 덧붙이고 [확인]


SDK 템플릿 Visual Studio로 복사

1. D:\gstreamer\1.0\x86_64\share\vs\2010\wizard의 파일 3개 복사

 - gst-sdk-template.ico

 - gst-sdk-template.vsdir

 - gst-sdk-template.vsz

2. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcprojects에 붙여넣기

3. D:\gstreamer\1.0\x86_64\share\vs\2010\gst-template 폴더 복사

4. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\VCWizards에 붙여넣기



Windows DDK 7.1.0 설치

1. 다운로드

2. 설치 후 경로 확인 (C:\WinDDK\7600.16385.1)



Visual Studio 10 설정

1. Visual Studio 10 실행

2. New > Project > Visual C++ > gstreamer template 선택하고 프로젝트 생성


3. 프로젝트 속성 > 구성 속성 > 디버깅 > 작업 디렉토리 > 편집 >  $(GSTREAMER_1_0_ROOT_X86_64)\bin 입력 > 확인


4. 프로젝트 속성 > 구성 속성 > C/C++ > 일반 > 추가 포함 디렉터리 > 편집에서 GStreamer 경로가 포함된 것을 확인


만약 포함되어 있지 않다면, 속성 관리자 > 프로젝트 > 기존 속성 시트 추가 > D:\gstreamer\1.0\x86_64\share\vs\2010\msvc 폴더의 x86.props(32비트) 또는 x86_64.props(64비트) 열기




5. 속성 관리자 > Debug > x86_64 > config > DDK 경로 들어간 것을 확인


6. Hello World 코드 빌드 및 실행

#include <gst/gst.h>


int main(int argc, char *argv[]) {

  GstElement *pipeline;

  GstBus *bus;

  GstMessage *msg;


  /* Initialize GStreamer */

  gst_init (&argc, &argv);


  /* Build the pipeline */

  pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);


  /* Start playing */

  gst_element_set_state (pipeline, GST_STATE_PLAYING);


  /* Wait until error or EOS */

  bus = gst_element_get_bus (pipeline);

  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);


  /* Free resources */

  if (msg != NULL)

    gst_message_unref (msg);

  gst_object_unref (bus);

  gst_element_set_state (pipeline, GST_STATE_NULL);

  gst_object_unref (pipeline);

  return 0;

}



참고

  • https://gstreamer.freedesktop.org/documentation/installing/on-windows.html


Dockerfile for gstreamer 1.10.4

Docker 2017.02.28 18:41 Posted by 김한별 behonestar

ubuntu 16.04 Docker 이미지에 gstreamer를 설치하는 Dockerfile입니다.

필요하지 않은 플러그인 설치 코드는 제거하고 사용하시면 됩니다.


FROM ubuntu:16.04


RUN apt-get -y update

RUN apt-get install -y vim

RUN apt-get install -y bison

RUN apt-get install -y g++

RUN apt-get install -y flex

RUN apt-get install -y wget

RUN apt-get install -y pkg-config

RUN apt-get install -y gettext

RUN apt-get install -y zlib1g-dev

RUN apt-get install -y yasm

RUN apt-get install -y libpcre3 libpcre3-dev


RUN apt-get install -y python2.7

RUN apt-get install -y python-pip

RUN export PYTHON=/usr/bin/python2.7


# install glib

WORKDIR /root

RUN wget ftp://sourceware.org/pub/libffi/libffi-3.2.tar.gz

RUN tar -zxvf libffi-3.2.tar.gz

WORKDIR /root/libffi-3.2

RUN sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' \

    -i include/Makefile.in && sed -e '/^includedir/ s/=.*$/=@includedir@/' \

    -e 's/^Cflags: -I${includedir}/Cflags:/' \

    -i libffi.pc.in && ./configure --prefix=/usr --disable-static && make

RUN make install


WORKDIR /root

RUN wget http://ftp.gnome.org/pub/gnome/sources/glib/2.48/glib-2.48.2.tar.xz

RUN tar xvf glib-2.48.2.tar.xz

WORKDIR /root/glib-2.48.2

RUN ./configure

RUN make && make install


# install gstreamer

WORKDIR /root

RUN wget http://liboil.freedesktop.org/download/liboil-0.3.17.tar.gz

RUN tar -zxvf liboil-0.3.17.tar.gz

WORKDIR /root/liboil-0.3.17

RUN ./configure

RUN make && make install


WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-1.10.4.tar.xz

RUN tar -xvf gstreamer-1.10.4.tar.xz

WORKDIR /root/gstreamer-1.10.4

RUN ./configure

RUN make && make install


# install plugins

WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/data/src/gst-plugins-base/gst-plugins-base-1.10.4.tar.xz

RUN tar -xvf gst-plugins-base-1.10.4.tar.xz

WORKDIR /root/gst-plugins-base-1.10.4

RUN ./configure

RUN make && make install


WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.10.4.tar.xz

RUN tar -xvf gst-plugins-good-1.10.4.tar.xz

WORKDIR /root/gst-plugins-good-1.10.4

RUN ./configure

RUN make && make install


WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/data/src/gst-plugins-bad/gst-plugins-bad-1.10.4.tar.xz

RUN tar -xvf gst-plugins-bad-1.10.4.tar.xz

WORKDIR /root/gst-plugins-bad-1.10.4

RUN ./configure

RUN make && make install


WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.10.4.tar.xz

RUN tar -xvf gst-plugins-ugly-1.10.4.tar.xz

WORKDIR /root/gst-plugins-ugly-1.10.4

RUN ./configure

RUN make && make install


# install libav

WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gst-libav/gst-libav-1.10.4.tar.xz

RUN tar -xvf gst-libav-1.10.4.tar.xz

WORKDIR /root/gst-libav-1.10.4

RUN ./configure

RUN make && make install


# install gst-rtsp-server

WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.10.4.tar.xz

RUN tar -xvf gst-rtsp-server-1.10.4.tar.xz

WORKDIR /root/gst-rtsp-server-1.10.4

RUN ./configure

RUN make && make install


# install gst-rtsp

WORKDIR /root

RUN wget https://gstreamer.freedesktop.org/src/gst-rtsp/gst-rtsp-server-1.10.4.tar.xz

RUN tar -xvf gst-rtsp-server-1.10.4.tar.xz

WORKDIR /root/gst-rtsp-server-1.10.4

RUN ./configure

RUN make && make install


# install gst-python
RUN apt-get install -y libgirepository1.0-dev
RUN apt-get install -y python-cairo-dev
RUN apt-get install -y libcairo2-dev

WORKDIR /root
RUN wget http://ftp.gnome.org/pub/GNOME/sources/pygobject/3.10/pygobject-3.10.2.tar.xz
RUN tar -xvf pygobject-3.10.2.tar.xz
WORKDIR /root/pygobject-3.10.2
RUN ./configure
RUN make && make install

WORKDIR /root
RUN wget https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.10.4.tar.xz
RUN tar -xvf gst-python-1.10.4.tar.xz
WORKDIR /root/gst-python-1.10.4
RUN ./configure
RUN make && make install

WORKDIR /root
RUN ln -s /usr/lib/x86_64-linux-gnu/libpython2.7.so /usr/lib/libpython2.7.so
RUN ldconfig