이 글은 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' 카테고리의 다른 글
[GStreamer] RTSP의 H264 영상 Dump 방법 (1) | 2017.03.29 |
---|---|
[GStreamer] 디버깅 로그 출력하기 (0) | 2017.03.29 |
[GStreamer] h264 profile 확인하는 방법 (0) | 2017.03.27 |
[GStreamer] 윈도우 Visual Studio 개발 환경 설정 (2) | 2017.03.06 |
[GStreamer] Pad-added Signal (0) | 2017.03.06 |