mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
pwg: flesh out allocation docs
Add more examples. Add example for implementing new metadata. Add programs to the docs (again?), it seems to contain useful info.
This commit is contained in:
parent
98dc4a057d
commit
bd0eb629bf
3 changed files with 443 additions and 36 deletions
|
@ -79,7 +79,7 @@ main (int argc, char *argv[])
|
|||
GError *err = NULL; /* error to show to users */
|
||||
gchar *dbg = NULL; /* additional debug string for developers */
|
||||
|
||||
gst_message_parse_error (msg, &err, &dbg);
|
||||
gst_message_parse_error (msg, &err, &dbg);
|
||||
if (err) {
|
||||
g_printerr ("ERROR: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
|
@ -193,6 +193,7 @@ gst-inspect mad
|
|||
</para>
|
||||
|
||||
<screen>
|
||||
<![CDATA[
|
||||
Factory Details:
|
||||
Rank: secondary (128)
|
||||
Long-name: Audio Sink (OSS)
|
||||
|
@ -325,6 +326,7 @@ Element Properties:
|
|||
device : OSS device (usually /dev/dspN)
|
||||
flags: readable, writable
|
||||
String. Default: "/dev/dsp"
|
||||
]]>
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
<!ENTITY COMPONENTS SYSTEM "highlevel-components.xml">
|
||||
|
||||
<!-- Appendices -->
|
||||
<!ENTITY PROGRAMS SYSTEM "appendix-programs.xml">
|
||||
<!ENTITY CHECKLIST SYSTEM "appendix-checklist.xml">
|
||||
<!ENTITY PORTING SYSTEM "appendix-porting.xml">
|
||||
<!ENTITY INTEGRATION SYSTEM "appendix-integration.xml">
|
||||
|
@ -253,6 +254,7 @@
|
|||
- table please...
|
||||
-->
|
||||
|
||||
&PROGRAMS;
|
||||
&CHECKLIST;
|
||||
&PORTING;
|
||||
&INTEGRATION;
|
||||
|
|
|
@ -41,20 +41,60 @@
|
|||
never be changed after the object is created, however, the offset
|
||||
and size can be changed.
|
||||
</para>
|
||||
<para>
|
||||
Data access to the memory wrapped by the <classname>GstMemory</classname>
|
||||
object is always protected with a <function>gst_memory_map()</function>
|
||||
and <function>gst_memory_unmap()</function> pair. An access mode
|
||||
(read/write) must be given when mapping memory. The map
|
||||
function returns a pointer to the valid memory region that can
|
||||
then be accessed according to the requested access mode.
|
||||
</para>
|
||||
<para>
|
||||
<classname>GstMemory</classname> objects are created by a
|
||||
<classname>GstAllocator</classname> object. To implement support
|
||||
for a new kind of memory type, you must implement a new allocator
|
||||
object.
|
||||
</para>
|
||||
<sect2 id="section-allocation-memory-ex" xreflabel="GstMemory-ex">
|
||||
<title>GstMemory API example</title>
|
||||
<para>
|
||||
Data access to the memory wrapped by the <classname>GstMemory</classname>
|
||||
object is always protected with a <function>gst_memory_map()</function>
|
||||
and <function>gst_memory_unmap()</function> pair. An access mode
|
||||
(read/write) must be given when mapping memory. The map
|
||||
function returns a pointer to the valid memory region that can
|
||||
then be accessed according to the requested access mode.
|
||||
</para>
|
||||
<para>
|
||||
Below is an example of making a <classname>GstMemory</classname>
|
||||
object and using the <function>gst_memory_map()</function> to
|
||||
access the memory region.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
[...]
|
||||
|
||||
GstMemory *mem;
|
||||
GstMapInfo info;
|
||||
gint i;
|
||||
|
||||
/* allocate 100 bytes */
|
||||
mem = gst_allocator_alloc (NULL, 100, NULL);
|
||||
|
||||
/* get access to the memory in write mode */
|
||||
gst_memory_map (mem, &info, GST_MAP_WRITE);
|
||||
|
||||
/* fill with pattern */
|
||||
for (i = 0; i < info.size; i++)
|
||||
info.data[i] = i;
|
||||
|
||||
/* release memory */
|
||||
gst_memory_unmap (mem, &info);
|
||||
|
||||
[...]
|
||||
]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="section-allocation-allocator" xreflabel="GstAllocator">
|
||||
<title>Implementing a GstAllocator</title>
|
||||
<para>
|
||||
WRITEME
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="section-allocation-buffer" xreflabel="GstBuffer">
|
||||
|
@ -100,14 +140,69 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
A buffer is writable when the refcount of the object is exactly 1, meaning
|
||||
that only one object is holding a ref to the buffer. You can only
|
||||
modify anything in the buffer when the buffer is writable. This means
|
||||
that you need to call <function>gst_buffer_make_writable()</function>
|
||||
before changing the timestamps, offsets, metadata or adding and
|
||||
removing memory blocks.
|
||||
</para>
|
||||
|
||||
<sect2 id="section-allocation-writability" xreflabel="GstBuffer-write">
|
||||
<title>GstBuffer writability</title>
|
||||
<para>
|
||||
A buffer is writable when the refcount of the object is exactly 1, meaning
|
||||
that only one object is holding a ref to the buffer. You can only
|
||||
modify anything in the buffer when the buffer is writable. This means
|
||||
that you need to call <function>gst_buffer_make_writable()</function>
|
||||
before changing the timestamps, offsets, metadata or adding and
|
||||
removing memory blocks.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="section-allocation-buffer-ex" xreflabel="GstBuffer-ex">
|
||||
<title>GstBuffer API examples</title>
|
||||
<para>
|
||||
You can create a buffer with <function>gst_buffer_new ()</function>
|
||||
and then add memory objects to it or you can use a convenience function
|
||||
<function>gst_buffer_new_allocate ()</function> which combines the
|
||||
two. It's also possible to wrap existing memory with
|
||||
<function>gst_buffer_new_wrapped_full () </function> where you can
|
||||
give the function to call when the memory should be freed.
|
||||
</para>
|
||||
<para>
|
||||
You can access the memory of the buffer by getting and mapping the
|
||||
<classname>GstMemory</classname> objects individually or by using
|
||||
<function>gst_buffer_map ()</function>. The latter merges all the
|
||||
memory into one big block and then gives you a pointer to this block.
|
||||
</para>
|
||||
<para>
|
||||
Below is an example of how to create a buffer and access its memory.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
[...]
|
||||
GstBuffer *buffer;
|
||||
GstMemory *mem;
|
||||
GstMapInfo info;
|
||||
|
||||
/* make empty buffer */
|
||||
buffer = gst_buffer_new ();
|
||||
|
||||
/* make memory holding 100 bytes */
|
||||
mem = gst_allocator_alloc (NULL, 100, NULL);
|
||||
|
||||
/* add the the buffer */
|
||||
gst_buffer_append_memory (buffer, mem);
|
||||
|
||||
[...]
|
||||
|
||||
/* get WRITE access to the memory and fill with 0xff */
|
||||
gst_buffer_map (buffer, &info, GST_MAP_WRITE);
|
||||
memset (info.data, 0xff, info.size);
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
|
||||
[...]
|
||||
|
||||
/* free the buffer */
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
[...]
|
||||
]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="section-allocation-meta" xreflabel="GstMeta">
|
||||
|
@ -122,6 +217,288 @@
|
|||
backing up the memory of the buffer. This makes it easier for elements
|
||||
to locate the X image from the buffer.
|
||||
</para>
|
||||
<para>
|
||||
The metadata system separates API specification (what the metadata
|
||||
and its API look like) and the implementation (how it works). This makes
|
||||
it possible to make different implementations of the same API,
|
||||
for example, depending on the hardware you are running on.
|
||||
</para>
|
||||
|
||||
<sect2 id="section-allocation-meta-ex" xreflabel="GstMeta-ex">
|
||||
<title>GstMeta API example</title>
|
||||
<para>
|
||||
After allocating a new buffer, you can add metadata to the buffer
|
||||
with the metadata specific API. This means that you will need to
|
||||
link to the header file where the metadata is defined to use
|
||||
its API.
|
||||
</para>
|
||||
<para>
|
||||
By convention, a metadata API with name <classname>FooBar</classname>
|
||||
should provide two methods, a
|
||||
<function>gst_buffer_add_foo_bar_meta ()</function> and a
|
||||
<function>gst_buffer_get_foo_bar_meta ()</function>. Both functions
|
||||
should return a pointer to a <classname>FooBarMeta</classname>
|
||||
structure that contains the metadata fields. Some of the
|
||||
<function>_add_*_meta ()</function> can have extra parameters that
|
||||
will usually be used to configure the metadata structure for you.
|
||||
</para>
|
||||
<para>
|
||||
Let's have a look at the metadata that is used to specify a cropping
|
||||
region for video frames.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#include <gst/video/gstvideometa.h>
|
||||
|
||||
[...]
|
||||
GstVideoCropMeta *meta;
|
||||
|
||||
/* buffer points to a video frame, add some cropping metadata */
|
||||
meta = gst_buffer_add_video_crop_meta (buffer);
|
||||
|
||||
/* configure the cropping metadata */
|
||||
meta->x = 8;
|
||||
meta->y = 8;
|
||||
meta->width = 120;
|
||||
meta->height = 80;
|
||||
[...]
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
An element can then use the metadata on the buffer when rendering
|
||||
the frame like this:
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#include <gst/video/gstvideometa.h>
|
||||
|
||||
[...]
|
||||
GstVideoCropMeta *meta;
|
||||
|
||||
/* buffer points to a video frame, get the cropping metadata */
|
||||
meta = gst_buffer_get_video_crop_meta (buffer);
|
||||
|
||||
if (meta) {
|
||||
/* render frame with cropping */
|
||||
_render_frame_cropped (buffer, meta->x, meta->y, meta->width, meta->height);
|
||||
} else {
|
||||
/* render frame */
|
||||
_render_frame (buffer);
|
||||
}
|
||||
[...]
|
||||
|
||||
]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="section-allocation-meta-new" xreflabel="GstMeta-new">
|
||||
<title>Implementing new GstMeta</title>
|
||||
<para>
|
||||
In the next sections we show how you can add new metadata to the
|
||||
system and use it on buffers.
|
||||
</para>
|
||||
|
||||
<sect3 id="section-allocation-meta-api" xreflabel="GstMeta-api">
|
||||
<title>Define the metadata API</title>
|
||||
<para>
|
||||
First we need to define what our API will look like and we
|
||||
will have to register this API to the system. This is important
|
||||
because this API definition will be used when elements negotiate
|
||||
what kind of metadata they will exchange. The API definition
|
||||
also contains arbitrary tags that give hints about what the
|
||||
metadata contains. This is important when we see how metadata
|
||||
is preserved when buffers pass through the pipeline.
|
||||
</para>
|
||||
<para>
|
||||
If you are making a new implementation of an existing API,
|
||||
you can skip this step and move on to the implementation step.
|
||||
</para>
|
||||
<para>
|
||||
First we start with making the
|
||||
<filename>my-example-meta.h</filename> header file that will contain
|
||||
the definition of the API and structure for our metadata.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#include <gst/gst.h>
|
||||
|
||||
typedef struct _MyExampleMeta MyExampleMeta;
|
||||
|
||||
struct _MyExampleMeta {
|
||||
GstMeta meta;
|
||||
|
||||
gint age;
|
||||
gchar *name;
|
||||
};
|
||||
|
||||
GType my_example_meta_api_get_type (void);
|
||||
#define MY_EXAMPLE_META_API_TYPE (my_example_meta_api_get_type())
|
||||
|
||||
#define gst_buffer_get_my_example_meta(b) \
|
||||
((MyExampleMeta*)gst_buffer_get_meta((b),MY_EXAMPLE_META_API_TYPE))
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
The metadata API definition consists of the definition of the
|
||||
structure that holds a gint and a string. The first field in
|
||||
the structure must be <classname>GstMeta</classname>.
|
||||
</para>
|
||||
<para>
|
||||
We also define a <function>my_example_meta_api_get_type ()</function>
|
||||
function that will register out metadata API definition. We
|
||||
also define a convenience macro
|
||||
<function>gst_buffer_get_my_example_meta ()</function> that simply
|
||||
finds and returns the metadata with our new API.
|
||||
</para>
|
||||
<para>
|
||||
Next let's have a look at how the
|
||||
<function>my_example_meta_api_get_type ()</function> function is
|
||||
implemented in the <filename>my-example-meta.c</filename> file.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#include "my-example-meta.h"
|
||||
|
||||
GType
|
||||
my_example_meta_api_get_type (void)
|
||||
{
|
||||
static volatile GType type;
|
||||
static const gchar *tags[] = { "foo", "bar", NULL };
|
||||
|
||||
if (g_once_init_enter (&type)) {
|
||||
GType _type = gst_meta_api_type_register ("MyExampleMetaAPI", tags);
|
||||
g_once_init_leave (&type, _type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
As you can see, it simply uses the
|
||||
<function>gst_meta_api_type_register ()</function> function to
|
||||
register a name for the api and some tags. The result is a
|
||||
new pointer GType that defines the newly registered API.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3 id="section-allocation-meta-impl" xreflabel="GstMeta-impl">
|
||||
<title>Implementing a metadata API</title>
|
||||
<para>
|
||||
Next we can make an implementation for a registered metadata
|
||||
API GType. The implementation detail of a metadata API
|
||||
are kept in a <classname>GstMetaInfo</classname> structure
|
||||
that you will make available to the users of your metadata
|
||||
API implementation with a <function>my_example_meta_get_info ()</function>
|
||||
function and a convenience <function>MY_EXAMPLE_META_INFO</function>
|
||||
macro. You will also make a method to add your metadata
|
||||
implementation to a <classname>GstBuffer</classname>.
|
||||
Your <filename>my-example-meta.h</filename> header file will
|
||||
need thse additions:
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
[...]
|
||||
|
||||
/* implementation */
|
||||
const GstMetaInfo *my_example_meta_get_info (void);
|
||||
#define MY_EXAMPLE_META_INFO (my_example_meta_get_info())
|
||||
|
||||
MyExampleMeta * gst_buffer_add_my_example_meta (GstBuffer *buffer,
|
||||
gint age,
|
||||
const gchar *name);
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
Let's have a look at how these functions are
|
||||
implemented in the <filename>my-example-meta.c</filename> file.
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
[...]
|
||||
|
||||
static gboolean
|
||||
my_example_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
|
||||
{
|
||||
MyExampleMeta *emeta = (MyExampleMeta *) meta;
|
||||
|
||||
emeta->age = 0;
|
||||
emeta->name = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
my_example_meta_transform (GstBuffer * transbuf, GstMeta * meta,
|
||||
GstBuffer * buffer, GQuark type, gpointer data)
|
||||
{
|
||||
MyExampleMeta *emeta = (MyExampleMeta *) meta;
|
||||
|
||||
/* we always copy no matter what transform */
|
||||
gst_buffer_add_my_example_meta (transbuf, emeta->age, emeta->name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
my_example_meta_free (GstMeta * meta, GstBuffer * buffer)
|
||||
{
|
||||
MyExampleMeta *emeta = (MyExampleMeta *) meta;
|
||||
|
||||
g_free (emeta->name)
|
||||
emeta->name = NULL;
|
||||
}
|
||||
|
||||
const GstMetaInfo *
|
||||
my_example_meta_get_info (void)
|
||||
{
|
||||
static const GstMetaInfo *meta_info = NULL;
|
||||
|
||||
if (meta_info == NULL) {
|
||||
meta_info = gst_meta_register (MY_EXAMPLE_META_API_TYPE,
|
||||
"MyExampleMeta",
|
||||
sizeof (MyExampleMeta),
|
||||
my_example_meta_init,
|
||||
my_example_meta_free,
|
||||
my_example_meta_transform);
|
||||
}
|
||||
return meta_info;
|
||||
}
|
||||
|
||||
MyExampleMeta *
|
||||
gst_buffer_add_my_example_meta (GstBuffer *buffer,
|
||||
gint age,
|
||||
const gchar *name)
|
||||
{
|
||||
MyExampleMeta *meta;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
|
||||
|
||||
meta = (MyExampleMeta *) gst_buffer_add_meta (buffer,
|
||||
MY_EXAMPLE_META_INFO, NULL);
|
||||
|
||||
meta->age = age;
|
||||
meta->name = g_strdup (name);
|
||||
|
||||
return meta;
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
<function>gst_meta_register ()</function> registers the implementation
|
||||
details, like the API that you implement and the size of the
|
||||
metadata structure along with methods to initialize and free the
|
||||
memory area. You can also implement a transform function that will
|
||||
be called when a certain transformation (identified by the quark and
|
||||
quark specific data) is performed on a buffer.
|
||||
</para>
|
||||
<para>
|
||||
Lastly, you implement a <function>gst_buffer_add_*_meta()</function>
|
||||
that adds the metadata implementation to a buffer and sets the
|
||||
values of the metadata.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="section-allocation-bufferpool" xreflabel="GstBufferPool">
|
||||
|
@ -141,6 +518,21 @@
|
|||
to the buffers in the pool or such as enabling specific padding on
|
||||
the memory in the buffers.
|
||||
</para>
|
||||
|
||||
<sect2 id="section-allocation-pool-ex" xreflabel="GstBufferPool-ex">
|
||||
<title>GstBufferPool API example</title>
|
||||
<para>
|
||||
WRITEME
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="section-allocation-pool-new" xreflabel="GstBufferPool-new">
|
||||
<title>Implementing a new GstBufferPool</title>
|
||||
<para>
|
||||
WRITEME
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="section-allocation-query" xreflabel="GST_QUERY_ALLOCATION">
|
||||
|
@ -191,24 +583,35 @@
|
|||
from the available bufferpools, allocators and metadata how it will
|
||||
allocate buffers.
|
||||
</para>
|
||||
<para>
|
||||
In many baseclasses you will see the following virtual methods for
|
||||
influencing the allocation strategy:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>propose_allocation ()</function> should suggest
|
||||
allocation parameters for the upstream element.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>decide_allocation ()</function> should decide the
|
||||
allocation parameters from the suggestions received from
|
||||
downstream.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<sect2 id="section-allocation-query-ex" xreflabel="Allocation-ex">
|
||||
<title>ALLOCATION query example</title>
|
||||
<para>
|
||||
WRITEME
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="section-allocation-query-base" xreflabel="Allocation-base">
|
||||
<title>The ALLOCATION query in base classes</title>
|
||||
<para>
|
||||
In many baseclasses you will see the following virtual methods for
|
||||
influencing the allocation strategy:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>propose_allocation ()</function> should suggest
|
||||
allocation parameters for the upstream element.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>decide_allocation ()</function> should decide the
|
||||
allocation parameters from the suggestions received from
|
||||
downstream.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
|
Loading…
Reference in a new issue