Pad의 스케줄링 모드는 (source)에서 데이터를 검색하거나 (sink) pad에 데이터를 제공하는 방법을 정의합니다. GStreamer는 push 모드와 pull 모드라는 두 가지 스케줄링 모드에서 작동할 수 있습니다. GStreamer는 모든 pad가 동일한 모드에서 작동할 필요가 없는 스케줄링 모드에서 pad가 있는 element를 지원합니다.
지금까지 _chain() operating element, 즉 sink pad에 체인 함수이 설정되어 있고 source pad에 push 버퍼가 있는 element에 대해서만 설명했습니다. 이를 push 모드라고 부르는 이유는 peer element가 srcpad에서 gst_pad_push()를 사용하여 _chain() 함수가 호출되고, 그러면 element가 source pad에서 버퍼를 push하기 때문입니다. 데이터 흐름을 시작하는 이니셔티브는 버퍼를 push 할 때 업스트림 어딘가에서 발생하고, 모든 다운스트림 element는 _chain() 함수가 차례로 호출될 때 스케줄링됩니다.
Pull 모드 스케줄링을 설명하기에 앞서, 먼저 pad에서 다양한 스케줄링 모드가 어떻게 선택되고 활성화되는지 알아보겠습니다.
The pad activation stage
Element 상태가 READY->PAUSED로 변경되는 동안 element의 pad가 활성화됩니다. 이는 먼저 source pad에서 발생한 다음 element의 sink pad에서 발생합니다. GStreamer는 pad의 _activate()를 호출합니다. 기본적으로 이 함수는 GST_PAD_MODE_PUSH 스케줄링 모드로 gst_pad_activate_mode()를 호출하여 push 모드에서 pad를 활성화합니다. Pad의 _activate()를 재정의하고 다른 스케줄링 모드를 결정할 수 있습니다. _activate_mode() 함수를 재정의하여 pad가 어떤 스케줄링 모드에서 활성화되는지 알 수 있습니다.
GStreamer는 element의 다른 pad가 다른 스케줄링 모드에서 작동할 수 있도록 합니다. 이를 통해 다양한 사용 사례가 가능합니다. 다음은 몇 가지 일반적인 사용 사례에 대한 개요입니다.
- Element의 모든 pad가 push 모드 스케줄링에서 활성화되면 element 전체가 push 모드에서 작동합니다. Source element의 경우 이는 source pad의 버퍼를 다운스트림 element로 push하는 작업을 시작해야 함을 의미합니다. 다운스트림 element는 sinkpad _chain() 함수를 사용하여 업스트림 element에서 source pad의 버퍼를 push 하는 데이터를 push 합니다. 이 스케줄링 모드의 전제 조건은 gst_pad_set_chain_function()을 사용하여 각 sinkpad에 대한 체인 함수를 설정하고 모든 다운스트림 element가 동일한 모드에서 작동해야 한다는 것입니다.
- 또는 sinkpad는 pull 모드에서 작동하여 파이프라인의 원동력이 될 수 있으며 element의 sourcepad는 여전히 push 모드에서 작동합니다. 추진력이 되기 위해 이러한 pad는 활성화될 때 GstTask를 시작합니다. 이 작업은 스레드이며 element에서 지정한 함수를 호출합니다. 이 함수가 호출되면 모든 sinkpad에서 random data access (gst_pad_pull_range()를 통해)를 할 수 있으며 sourcepad에서 데이터를 push 할 수 있습니다. 즉, 이 element가 파이프라인에서 데이터 흐름을 제어한다는 의미입니다. 이 모드의 전제 조건은 모든 다운스트림 element가 push 모드에서 작동할 수 있고 모든 업스트림 element가 pull 모드에서 작동한다는 것입니다 (아래 참조).
Source pad는 GST_QUERY_SCHEDULING 쿼리에서 GST_PAD_MODE_PULL을 반환할 때 다운스트림 element에 의해 PULL 모드에서 활성화될 수 있습니다. 이 스케줄링 모드의 전제 조건은 gst_pad_set_getrange_function()을 사용하여 source pad에 대한 getrange 함수가 설정되었다는 것입니다. - 마지막으로 element의 모든 pad는 PULL 모드에서 활성화될 수 있습니다. 그러나 위의 내용과 달리 이는 스스로 작업을 시작한다는 것을 의미하지 않습니다. 오히려 다운스트림 element에 대한 pull slave 이며 _get_range() 함수에서 random data access를 제공해야 함을 의미합니다. 요구 사항은 gst_pad_set_getrange_function() 함수를 사용하여 이 pad에 _get_range() 함수가 설정되었다는 것입니다. 또한 element에 sinkpad가 있는 경우 모든 pad (및 해당 peer)도 PULL 액세스 모드에서 작동해야 합니다.
Sink element가 PULL 모드에서 활성화되면 sinkpad에서 gst_pad_pull_range()를 호출하는 작업을 시작해야 합니다. 업스트림 SCHEDULING 쿼리가 GST_PAD_MODE_PULL 스케줄링 모드에 대한 지원을 반환할 때만 이를 수행할 수 있습니다.
다음 두 섹션에서는 pull 모드 스케줄링 (파이프라인을 구동하는 element/pad와 random access를 제공하는 element/pad)에 대해 자세히 살펴보고 몇 가지 구체적인 사용 사례를 소개합니다.
Pads driving the pipeline
Pull 모드로 작동하는 sinkpad, push 모드로 작동하는 sourcepad (또는 sink일 때 sourcepad가 없는 경우)는 파이프라인 데이터 흐름을 구동하는 작업을 시작할 수 있습니다. 이 작업 함수 내에서 모든 sinkpad에 대한 random access가 가능하고 sourcepad에 데이터를 push 할 수 있습니다. 이는 여러 가지 다른 종류의 element에 유용할 수 있습니다.
- 데이터가 파싱되지 않은 상태로 들어오는 demuxer, parser 및 특정 종류의 decoder (예: MPEG 오디오 또는 비디오 스트림). 이 element들은 입력에서 바이트 단위 (random) access를 선호하기 때문입니다. 그러나 가능하다면 이러한 element는 push 모드에서도 작동하도록 준비해야 합니다.
- Jack 사운드 서버와 같이 입력 데이터 흐름을 제어해야 하는 특정 종류의 오디오를 출력하는 element.
먼저 업스트림 element가 pull 모드 스케줄링을 지원하는지 확인하기 위해 SCHEDULING 쿼리를 수행해야 합니다. 가능하다면 sinkpad를 pull 모드로 활성화할 수 있습니다. 그러면 activate_mode 함수 내부에서 작업을 시작할 수 있습니다.
#include "filter.h"
#include <string.h>
static gboolean gst_my_filter_activate (GstPad * pad,
GstObject * parent);
static gboolean gst_my_filter_activate_mode (GstPad * pad,
GstObject * parent,
GstPadMode mode,
gboolean active);
static void gst_my_filter_loop (GstMyFilter * filter);
G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);
static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);
gst_pad_set_activatemode_function (filter->sinkpad,
gst_my_filter_activate_mode);
[..]
}
[..]
static gboolean
gst_my_filter_activate (GstPad * pad, GstObject * parent)
{
GstQuery *query;
gboolean pull_mode;
/* first check what upstream scheduling is supported */
query = gst_query_new_scheduling ();
if (!gst_pad_peer_query (pad, query)) {
gst_query_unref (query);
goto activate_push;
}
/* see if pull-mode is supported */
pull_mode = gst_query_has_scheduling_mode_with_flags (query,
GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
gst_query_unref (query);
if (!pull_mode)
goto activate_push;
/* now we can activate in pull-mode. GStreamer will also
* activate the upstream peer in pull-mode */
return gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE);
activate_push:
{
/* something not right, we fallback to push-mode */
return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
}
}
static gboolean
gst_my_filter_activate_pull (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;
}
일단 시작하면, 당신의 작업은 입력과 출력을 완전히 제어할 수 있습니다. 작업 함수의 가장 간단한 사례는 입력을 읽고 그것을 source pad로 push 하는 것입니다. 그다지 유용하지는 않지만, 지금까지 살펴본 이전의 push 모드 사례보다 더 많은 유연성을 제공합니다.
#define BLOCKSIZE 2048
static void
gst_my_filter_loop (GstMyFilter * filter)
{
GstFlowReturn ret;
guint64 len;
GstBuffer *buf = NULL;
if (!gst_pad_query_duration (filter->sinkpad, GST_FORMAT_BYTES, &len)) {
GST_DEBUG_OBJECT (filter, "failed to query duration, pausing");
goto stop;
}
if (filter->offset >= len) {
GST_DEBUG_OBJECT (filter, "at end of input, sending EOS, pausing");
gst_pad_push_event (filter->srcpad, gst_event_new_eos ());
goto stop;
}
/* now, read BLOCKSIZE bytes from byte offset filter->offset */
ret = gst_pad_pull_range (filter->sinkpad, filter->offset,
BLOCKSIZE, &buf);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (filter, "pull_range failed: %s", gst_flow_get_name (ret));
goto stop;
}
/* now push buffer downstream */
ret = gst_pad_push (filter->srcpad, buf);
buf = NULL; /* gst_pad_push() took ownership of buffer */
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (filter, "pad_push failed: %s", gst_flow_get_name (ret));
goto stop;
}
/* everything is fine, increase offset and wait for us to be called again */
filter->offset += BLOCKSIZE;
return;
stop:
GST_DEBUG_OBJECT (filter, "pausing task");
gst_pad_pause_task (filter->sinkpad);
}
Providing random access
이전 섹션에서는 자체 작업을 사용하여 파이프라인을 구동하도록 활성화된 element (또는 pad)가 sinkpad에서 pull 모드 스케줄링을 사용해야 하는 방법에 대해 설명했습니다. 즉, 해당 pad에 연결된 모든 pad는 pull 모드에서 활성화되어야 합니다. Pull 모드에서 활성화된 source pad는 gst_pad_set_getrange_function()을 사용하여 _get_range() 함수 세트를 구현해야 하며, peer pad가 gst_pad_pull_range()로 일부 데이터를 요청할 때 해당 함수가 호출됩니다. 그런 다음 element는 올바른 오프셋을 찾고 요청된 데이터를 제공해야 합니다. 여러 element가 random access를 구현할 수 있습니다.
- 파일 source와 같은 데이터 source는 합리적인 낮은 지연 시간으로 모든 오프셋에서 데이터를 제공할 수 있습니다.
- 전체 파이프라인에 대해 pull 모드 스케줄링을 제공하고자 하는 filter.
- 입력의 일부를 건너뛰어 이를 쉽게 제공할 수 있는 parser이며, 따라서 본질적으로 자체 처리 없이 문자 그대로 getrange 요청을 "전달"합니다. 예로는 태그 리더 (예: ID3) 또는 WAVE 파서와 같은 단일 출력 파서가 있습니다.
다음 예제는 _get_range() 함수를 source element에서 구현하는 방법을 보여줍니다.
#include "filter.h"
static GstFlowReturn
gst_my_filter_get_range (GstPad * pad,
GstObject * parent,
guint64 offset,
guint length,
GstBuffer ** buf);
G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);
static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
gst_pad_set_getrange_function (filter->srcpad,
gst_my_filter_get_range);
[..]
}
static GstFlowReturn
gst_my_filter_get_range (GstPad * pad,
GstObject * parent,
guint64 offset,
guint length,
GstBuffer ** buf)
{
GstMyFilter *filter = GST_MY_FILTER (parent);
[.. here, you would fill *buf ..]
return GST_FLOW_OK;
}
실제로, 이론적으로 random acces를 할 수 있는 많은 element들은 실제로는 자체 작업을 시작할 수 있는 다운스트림 element가 없기 때문에 push 모드 스케줄링에서 어차피 활성화될 수 있습니다. 따라서 실제로 이러한 element들은 _get_range() 함수와 _chain() 함수 (filter 및 parser를 위한) 또는 _get_range() 함수를 모두 구현해야 하며 _activate_*() 함수(source element를 위한)를 제공하여 자체 작업을 시작할 준비가 되어 있어야 합니다.
원문: Different scheduling modes
Different scheduling modes
Different scheduling modes The scheduling mode of a pad defines how data is retrieved from (source) or given to (sink) pads. GStreamer can operate in two scheduling mode, called push- and pull-mode. GStreamer supports elements with pads in any of the sched
gstreamer.freedesktop.org
'IT와 개발 > GStreamer Study' 카테고리의 다른 글
Caps negotiation (1) | 2024.11.29 |
---|---|
Request and Sometimes pads (6) | 2024.10.18 |
Signals (1) | 2024.10.11 |
Building a Test Application (7) | 2024.10.11 |
Adding Properties (1) | 2024.10.04 |