mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 04:36:20 +00:00
pwg: rework dynamic pads docs
This commit is contained in:
parent
723c2a48b9
commit
3cc8f1412d
1 changed files with 77 additions and 54 deletions
|
@ -53,17 +53,18 @@ typedef struct _GstMyFilter {
|
||||||
GList *srcpadlist;
|
GList *srcpadlist;
|
||||||
} GstMyFilter;
|
} GstMyFilter;
|
||||||
|
|
||||||
|
static GstStaticPadTemplate src_factory =
|
||||||
|
GST_STATIC_PAD_TEMPLATE (
|
||||||
|
"src_%u",
|
||||||
|
GST_PAD_SRC,
|
||||||
|
GST_PAD_SOMETIMES,
|
||||||
|
GST_STATIC_CAPS ("ANY")
|
||||||
|
);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_my_filter_base_init (GstMyFilterClass *klass)
|
gst_my_filter_class_init (GstMyFilterClass *klass)
|
||||||
{
|
{
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
static GstStaticPadTemplate src_factory =
|
|
||||||
GST_STATIC_PAD_TEMPLATE (
|
|
||||||
"src_%02d",
|
|
||||||
GST_PAD_SRC,
|
|
||||||
GST_PAD_SOMETIMES,
|
|
||||||
GST_STATIC_CAPS ("ANY")
|
|
||||||
);
|
|
||||||
[..]
|
[..]
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
gst_static_pad_template_get (&src_factory));
|
gst_static_pad_template_get (&src_factory));
|
||||||
|
@ -96,11 +97,11 @@ gst_my_filter_getline (GstMyFilter *filter)
|
||||||
|
|
||||||
/* newline? */
|
/* newline? */
|
||||||
if (data[n] == '\n') {
|
if (data[n] == '\n') {
|
||||||
GstBuffer *buf = gst_buffer_new_and_alloc (n + 1);
|
GstBuffer *buf = gst_buffer_new_allocate (NULL, n + 1, NULL);
|
||||||
|
|
||||||
gst_bytestream_peek_bytes (filter->bs, &data, n);
|
gst_bytestream_peek_bytes (filter->bs, &data, n);
|
||||||
memcpy (GST_BUFFER_DATA (buf), data, n);
|
gst_buffer_fill (buf, 0, data, n);
|
||||||
GST_BUFFER_DATA (buf)[n] = '\0';
|
gst_buffer_memset (buf, n, '\0', 1);
|
||||||
gst_bytestream_flush_fast (filter->bs, n + 1);
|
gst_bytestream_flush_fast (filter->bs, n + 1);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
|
@ -114,31 +115,33 @@ gst_my_filter_loopfunc (GstElement *element)
|
||||||
GstMyFilter *filter = GST_MY_FILTER (element);
|
GstMyFilter *filter = GST_MY_FILTER (element);
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
GstPad *pad;
|
GstPad *pad;
|
||||||
|
GstMapInfo map;
|
||||||
gint num, n;
|
gint num, n;
|
||||||
|
|
||||||
/* parse header */
|
/* parse header */
|
||||||
if (filter->firstrun) {
|
if (filter->firstrun) {
|
||||||
GstElementClass *klass;
|
|
||||||
GstPadTemplate *templ;
|
|
||||||
gchar *padname;
|
gchar *padname;
|
||||||
|
guint8 id;
|
||||||
|
|
||||||
if (!(buf = gst_my_filter_getline (filter))) {
|
if (!(buf = gst_my_filter_getline (filter))) {
|
||||||
gst_element_error (element, STREAM, READ, (NULL),
|
gst_element_error (element, STREAM, READ, (NULL),
|
||||||
("Stream contains no header"));
|
("Stream contains no header"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
num = atoi (GST_BUFFER_DATA (buf));
|
gst_buffer_extract (buf, 0, &id, 1);
|
||||||
|
num = atoi (id);
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
|
|
||||||
/* for each of the streams, create a pad */
|
/* for each of the streams, create a pad */
|
||||||
klass = GST_ELEMENT_GET_CLASS (filter);
|
|
||||||
templ = gst_element_class_get_pad_template (klass, "src_%02d");
|
|
||||||
for (n = 0; n < num; n++) {
|
for (n = 0; n < num; n++) {
|
||||||
padname = g_strdup_printf ("src_%02d", n);
|
padname = g_strdup_printf ("src_%u", n);
|
||||||
pad = gst_pad_new_from_template (templ, padname);
|
pad = gst_pad_new_from_static_template (src_factory, padname);
|
||||||
g_free (padname);
|
g_free (padname);
|
||||||
|
|
||||||
/* here, you would set _getcaps () and _link () functions */
|
/* 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);
|
gst_element_add_pad (element, pad);
|
||||||
filter->srcpadlist = g_list_append (filter->srcpadlist, pad);
|
filter->srcpadlist = g_list_append (filter->srcpadlist, pad);
|
||||||
|
@ -153,36 +156,37 @@ gst_my_filter_loopfunc (GstElement *element)
|
||||||
for (padlist = srcpadlist;
|
for (padlist = srcpadlist;
|
||||||
padlist != NULL; padlist = g_list_next (padlist)) {
|
padlist != NULL; padlist = g_list_next (padlist)) {
|
||||||
pad = GST_PAD (padlist->data);
|
pad = GST_PAD (padlist->data);
|
||||||
gst_event_ref (event);
|
gst_pad_push_event (pad, gst_event_ref (event));
|
||||||
gst_pad_push (pad, GST_DATA (event));
|
|
||||||
}
|
}
|
||||||
gst_event_unref (event);
|
gst_event_unref (event);
|
||||||
gst_element_set_eos (element);
|
/* pause the task here */
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parse stream number and go beyond the ':' in the data */
|
/* parse stream number and go beyond the ':' in the data */
|
||||||
num = atoi (GST_BUFFER_DATA (buf));
|
gst_buffer_map (buf, &map, GST_MAP_READ);
|
||||||
|
num = atoi (map.data[0]);
|
||||||
if (num >= 0 && num < g_list_length (filter->srcpadlist)) {
|
if (num >= 0 && num < g_list_length (filter->srcpadlist)) {
|
||||||
pad = GST_PAD (g_list_nth_data (filter->srcpadlist, num);
|
pad = GST_PAD (g_list_nth_data (filter->srcpadlist, num);
|
||||||
|
|
||||||
/* magic buffer parsing foo */
|
/* magic buffer parsing foo */
|
||||||
for (n = 0; GST_BUFFER_DATA (buf)[n] != ':' &&
|
for (n = 0; map.data[n] != ':' &&
|
||||||
GST_BUFFER_DATA (buf)[n] != '\0'; n++) ;
|
map.data[n] != '\0'; n++) ;
|
||||||
if (GST_BUFFER_DATA (buf)[n] != '\0') {
|
if (map.data[n] != '\0') {
|
||||||
GstBuffer *sub;
|
GstBuffer *sub;
|
||||||
|
|
||||||
/* create subbuffer that starts right past the space. The reason
|
/* create region copy that starts right past the space. The reason
|
||||||
* that we don't just forward the data pointer is because the
|
* that we don't just forward the data pointer is because the
|
||||||
* pointer is no longer the start of an allocated block of memory,
|
* 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.
|
* 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
|
* That cannot be freed upon disposal, so we'd either crash or have
|
||||||
* a memleak. Creating a subbuffer is a simple way to solve that. */
|
* a memleak. Creating a region copy is a simple way to solve that. */
|
||||||
sub = gst_buffer_create_sub (buf, n + 1, GST_BUFFER_SIZE (buf) - n - 1);
|
sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
|
||||||
gst_pad_push (pad, GST_DATA (sub));
|
n + 1, map.size - n - 1);
|
||||||
|
gst_pad_push (pad, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
gst_buffer_unmap (buf, &map);
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
}
|
}
|
||||||
]]>
|
]]>
|
||||||
|
@ -207,46 +211,52 @@ gst_my_filter_loopfunc (GstElement *element)
|
||||||
where - for each elementary stream that is to be placed in the output
|
where - for each elementary stream that is to be placed in the output
|
||||||
system stream - one sink pad will be requested. It can also be used in
|
system stream - one sink pad will be requested. It can also be used in
|
||||||
elements with a variable number of input or outputs pads, such as the
|
elements with a variable number of input or outputs pads, such as the
|
||||||
<classname>tee</classname> (multi-output), <classname>switch</classname>
|
<classname>tee</classname> (multi-output) or
|
||||||
or <classname>aggregator</classname> (both multi-input) elements. At the
|
<classname>input-selector</classname> (multi-input) elements.
|
||||||
time of writing this, it is unclear to me who is responsible for cleaning
|
</para>
|
||||||
up the created pad and how or when that should be done. Below is a simple
|
<para>
|
||||||
example of an aggregator based on request pads.
|
To implement request pads, you need to provide a padtemplate with a
|
||||||
|
GST_PAD_REQUEST presence and implement the
|
||||||
|
<function>request_new_pad</function> virtual method in
|
||||||
|
<classname>GstElement</classname>.
|
||||||
|
To clean up, you will need to implement the
|
||||||
|
<function>release_pad</function> virtual method.
|
||||||
</para>
|
</para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
static GstPad * gst_my_filter_request_new_pad (GstElement *element,
|
static GstPad * gst_my_filter_request_new_pad (GstElement *element,
|
||||||
GstPadTemplate *templ,
|
GstPadTemplate *templ,
|
||||||
const gchar *name);
|
const gchar *name,
|
||||||
|
const GstCaps *caps);
|
||||||
|
|
||||||
static void
|
static void gst_my_filter_release_pad (GstElement *element,
|
||||||
gst_my_filter_base_init (GstMyFilterClass *klass)
|
GstPad *pad);
|
||||||
{
|
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
static GstStaticPadTemplate sink_factory =
|
||||||
static GstStaticPadTemplate sink_factory =
|
GST_STATIC_PAD_TEMPLATE (
|
||||||
GST_STATIC_PAD_TEMPLATE (
|
"sink_%u",
|
||||||
"sink_%d",
|
GST_PAD_SINK,
|
||||||
GST_PAD_SINK,
|
GST_PAD_REQUEST,
|
||||||
GST_PAD_REQUEST,
|
GST_STATIC_CAPS ("ANY")
|
||||||
GST_STATIC_CAPS ("ANY")
|
);
|
||||||
);
|
|
||||||
[..]
|
|
||||||
gst_element_class_add_pad_template (klass,
|
|
||||||
gst_static_pad_template_get (&sink_factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_my_filter_class_init (GstMyFilterClass *klass)
|
gst_my_filter_class_init (GstMyFilterClass *klass)
|
||||||
{
|
{
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (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->request_new_pad = gst_my_filter_request_new_pad;
|
||||||
|
element_class->release_pad = gst_my_filter_release_pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstPad *
|
static GstPad *
|
||||||
gst_my_filter_request_new_pad (GstElement *element,
|
gst_my_filter_request_new_pad (GstElement *element,
|
||||||
GstPadTemplate *templ,
|
GstPadTemplate *templ,
|
||||||
const gchar *name)
|
const gchar *name,
|
||||||
|
const GstCaps *caps)
|
||||||
{
|
{
|
||||||
GstPad *pad;
|
GstPad *pad;
|
||||||
GstMyFilterInputContext *context;
|
GstMyFilterInputContext *context;
|
||||||
|
@ -255,12 +265,25 @@ gst_my_filter_request_new_pad (GstElement *element,
|
||||||
pad = gst_pad_new_from_template (templ, name);
|
pad = gst_pad_new_from_template (templ, name);
|
||||||
gst_pad_set_element_private (pad, context);
|
gst_pad_set_element_private (pad, context);
|
||||||
|
|
||||||
/* normally, you would set _link () and _getcaps () functions here */
|
/* normally, you would set _chain () and _event () functions here */
|
||||||
|
|
||||||
gst_element_add_pad (element, pad);
|
gst_element_add_pad (element, pad);
|
||||||
|
|
||||||
return 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);
|
||||||
|
}
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
Loading…
Reference in a new issue