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:
Wim Taymans 2012-10-02 11:34:47 +02:00
parent 98dc4a057d
commit bd0eb629bf
3 changed files with 443 additions and 36 deletions

View file

@ -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>

View file

@ -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;

View file

@ -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>