GStreamer

[GStreamer] Push, Pull 방식

behonestar 2017. 4. 5. 16:08


이 글은 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;

}


끝.