지금까지 우리는 항상 사용 가능한 pad만 다루었습니다. 그러나 어떤 경우에만 생성되거나 애플리케이션에서 pad를 요청하는 경우에만 생성되는 pad도 있습니다. 첫 번째는 sometimes pad 라고 하며 두 번째는 request pad 라고 합니다. Pad의 사용 가능 여부 (always, sometimes 또는 request)는 pad의 템플릿에서 확인할 수 있습니다. 이 챕터에서는 두 가지가 각각 언제 유용한지, 어떻게 생성되는지, 언제 폐기해야 하는지에 대해 설명합니다.
Sometimes pads
"Sometimes" pad는 특정 조건에서 생성되지만 모든 경우에 생성되는 pad는 아닙니다. 이는 주로 스트림 콘텐츠에 따라 달라집니다. Demuxer는 일반적으로 스트림 헤더를 파싱하고 시스템 스트림에 포함된 기본 (비디오, 오디오, 자막 등) 스트림을 결정한 다음 각 기본 스트림에 대해 sometimes pad를 생성합니다. 자체 선택에 따라 element 인스턴스당 각 인스턴스를 두 개 이상 생성할 수도 있습니다. 유일한 제한 사항은 새로 생성된 각 pad가 고유한 이름을 가져야 한다는 것입니다. 스트림 데이터가 삭제될 때 pad도 삭제되는 경우가 있습니다 (예: PAUSED 상태에서 READY 상태로 전환될 때). EOS에서 pad를 삭제해서는 안 됩니다. 누군가가 파이프라인을 다시 활성화하고 스트림 종료 지점 이전으로 돌아갈 수 있기 때문입니다. 스트림은 EOS 이후에도 유효해야 하며, 적어도 스트림 데이터가 삭제될 때까지는 그렇습니다. 어떤 경우든 element는 항상 해당 pad의 소유자입니다.
아래 예제 코드는 첫 번째 줄이 숫자 (n)인 텍스트 파일을 파싱 합니다. 다음 줄은 모두 숫자(0~n-1)로 시작하는데, 이는 데이터를 전송할 source pad 번호입니다.
3
0: foo
1: bar
0: boo
2: bye
이 파일을 파싱하고 동적 "sometimes" pad를 생성하는 코드는 다음과 같습니다.
typedef struct _GstMyFilter {
[..]
gboolean firstrun;
GList *srcpadlist;
} GstMyFilter;
static GstStaticPadTemplate src_factory =
GST_STATIC_PAD_TEMPLATE (
"src_%u",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS ("ANY")
);
static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
[..]
}
static void
gst_my_filter_init (GstMyFilter *filter)
{
[..]
filter->firstrun = TRUE;
filter->srcpadlist = NULL;
}
/*
* Get one line of data - without newline.
*/
static GstBuffer *
gst_my_filter_getline (GstMyFilter *filter)
{
guint8 *data;
gint n, num;
/* max. line length is 512 characters - for safety */
for (n = 0; n < 512; n++) {
num = gst_bytestream_peek_bytes (filter->bs, &data, n + 1);
if (num != n + 1)
return NULL;
/* newline? */
if (data[n] == '\n') {
GstBuffer *buf = gst_buffer_new_allocate (NULL, n + 1, NULL);
gst_bytestream_peek_bytes (filter->bs, &data, n);
gst_buffer_fill (buf, 0, data, n);
gst_buffer_memset (buf, n, '\0', 1);
gst_bytestream_flush_fast (filter->bs, n + 1);
return buf;
}
}
}
static void
gst_my_filter_loopfunc (GstElement *element)
{
GstMyFilter *filter = GST_MY_FILTER (element);
GstBuffer *buf;
GstPad *pad;
GstMapInfo map;
gint num, n;
/* parse header */
if (filter->firstrun) {
gchar *padname;
guint8 id;
if (!(buf = gst_my_filter_getline (filter))) {
gst_element_error (element, STREAM, READ, (NULL),
("Stream contains no header"));
return;
}
gst_buffer_extract (buf, 0, &id, 1);
num = atoi (id);
gst_buffer_unref (buf);
/* for each of the streams, create a pad */
for (n = 0; n < num; n++) {
padname = g_strdup_printf ("src_%u", n);
pad = gst_pad_new_from_static_template (src_factory, padname);
g_free (padname);
/* here, you would set _event () and _query () functions */
/* need to activate the pad before adding */
gst_pad_set_active (pad, TRUE);
gst_element_add_pad (element, pad);
filter->srcpadlist = g_list_append (filter->srcpadlist, pad);
}
}
/* and now, simply parse each line and push over */
if (!(buf = gst_my_filter_getline (filter))) {
GstEvent *event = gst_event_new (GST_EVENT_EOS);
GList *padlist;
for (padlist = srcpadlist;
padlist != NULL; padlist = g_list_next (padlist)) {
pad = GST_PAD (padlist->data);
gst_pad_push_event (pad, gst_event_ref (event));
}
gst_event_unref (event);
/* pause the task here */
return;
}
/* parse stream number and go beyond the ':' in the data */
gst_buffer_map (buf, &map, GST_MAP_READ);
num = atoi (map.data[0]);
if (num >= 0 && num < g_list_length (filter->srcpadlist)) {
pad = GST_PAD (g_list_nth_data (filter->srcpadlist, num);
/* magic buffer parsing foo */
for (n = 0; map.data[n] != ':' &&
map.data[n] != '\0'; n++) ;
if (map.data[n] != '\0') {
GstBuffer *sub;
/* create region copy that starts right past the space. The reason
* that we don't just forward the data pointer is because the
* pointer is no longer the start of an allocated block of memory,
* but just a pointer to a position somewhere in the middle of it.
* That cannot be freed upon disposal, so we'd either crash or have
* a memleak. Creating a region copy is a simple way to solve that. */
sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
n + 1, map.size - n - 1);
gst_pad_push (pad, sub);
}
}
gst_buffer_unmap (buf, &map);
gst_buffer_unref (buf);
}
파일의 내용이 유효한지 확인하기 위해 모든 곳에서 많은 검사를 사용한다는 점에 유의하세요. 여기에는 두 가지 목적이 있습니다. 첫째, 파일에 오류가 있을 수 있으며, 이 경우 충돌을 방지합니다. 둘째이자 가장 중요한 이유는 극단적인 경우 파일이 플러그인에서 정의되지 않은 동작을 유발하는 데 악의적으로 사용될 수 있으며, 이는 보안 문제로 이어질 수 있기 때문입니다. 파일이 나쁜 일을 하는 데 사용될 수 있다고 항상 가정하세요.
Request pads
"Request" pad는 sometimes pad와 비슷하지만, request는 element 내부의 것이 아니라 element 외부의 무언가에 대한 요구에 따라 생성됩니다. 이 개념은 종종 muxer에서 사용되는데, 여기서는 출력 시스템 스트림에 배치될 각 기본 스트림에 대해 하나의 sink pad가 요청됩니다. 또한 tee (다중 출력) 또는 input-selector (다중 입력) element와 같이 입력 또는 출력 pad의 수가 가변적인 element에서도 사용할 수 있습니다.
Request pad를 구현하려면 GST_PAD_REQUEST가 있는 pad 템플릿을 제공하고 GstElement에서 request_new_pad 가상 메서드를 구현해야 합니다. 정리하려면 release_pad 가상 메서드를 구현해야 합니다.
static GstPad * gst_my_filter_request_new_pad (GstElement *element,
GstPadTemplate *templ,
const gchar *name,
const GstCaps *caps);
static void gst_my_filter_release_pad (GstElement *element,
GstPad *pad);
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
"sink_%u",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS ("ANY")
);
static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]
gst_element_class_add_pad_template (klass,
gst_static_pad_template_get (&sink_factory));
[..]
element_class->request_new_pad = gst_my_filter_request_new_pad;
element_class->release_pad = gst_my_filter_release_pad;
}
static GstPad *
gst_my_filter_request_new_pad (GstElement *element,
GstPadTemplate *templ,
const gchar *name,
const GstCaps *caps)
{
GstPad *pad;
GstMyFilterInputContext *context;
context = g_new0 (GstMyFilterInputContext, 1);
pad = gst_pad_new_from_template (templ, name);
gst_pad_set_element_private (pad, context);
/* normally, you would set _chain () and _event () functions here */
gst_element_add_pad (element, pad);
return pad;
}
static void
gst_my_filter_release_pad (GstElement *element,
GstPad *pad)
{
GstMyFilterInputContext *context;
context = gst_pad_get_element_private (pad);
g_free (context);
gst_element_remove_pad (element, pad);
}
원문: Request and Sometimes pads (gstreamer.freedesktop.org)
Request and Sometimes pads
Request and Sometimes pads Until now, we've only dealt with pads that are always available. However, there's also pads that are only being created in some cases, or only if the application requests the pad. The first is called a sometimes; the second is ca
gstreamer.freedesktop.org
'IT와 개발 > GStreamer Study' 카테고리의 다른 글
| Caps negotiation (1) | 2024.11.29 |
|---|---|
| Different scheduling modes (1) | 2024.11.01 |
| Signals (1) | 2024.10.11 |
| Building a Test Application (7) | 2024.10.11 |
| Adding Properties (1) | 2024.10.04 |