With the GstMeta
system you can add arbitrary
structures of on buffers. These structures describe extra properties
of the buffer such as cropping, stride, region of interest etc.
Metadata is also used to store, for example, the X image that is backing up the memory of the buffer. This makes it easier for elements to locate the X image from the buffer.
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.
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.
By convention, a metadata API with name FooBar
should provide two methods, a
gst_buffer_add_foo_bar_meta ()
and a
gst_buffer_get_foo_bar_meta ()
. Both functions
should return a pointer to a FooBarMeta
structure that contains the metadata fields. Some of the
_add_*_meta ()
can have extra parameters that
will usually be used to configure the metadata structure for you.
Let's have a look at the metadata that is used to specify a cropping region for video frames.
#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; [...]
An element can then use the metadata on the buffer when rendering the frame like this:
#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); } [...]
In the next sections we show how you can add new metadata to the system and use it on buffers.
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.
If you are making a new implementation of an existing API, you can skip this step and move on to the implementation step.
First we start with making the my-example-meta.h header file that will contain the definition of the API and structure for our metadata.
#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))
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 GstMeta
.
We also define a my_example_meta_api_get_type ()
function that will register out metadata API definition. We
also define a convenience macro
gst_buffer_get_my_example_meta ()
that simply
finds and returns the metadata with our new API.
Next let's have a look at how the
my_example_meta_api_get_type ()
function is
implemented in the my-example-meta.c file.
#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; }
As you can see, it simply uses the
gst_meta_api_type_register ()
function to
register a name for the api and some tags. The result is a
new pointer GType that defines the newly registered API.
Next we can make an implementation for a registered metadata
API GType. The implementation detail of a metadata API
are kept in a GstMetaInfo
structure
that you will make available to the users of your metadata
API implementation with a my_example_meta_get_info ()
function and a convenience MY_EXAMPLE_META_INFO
macro. You will also make a method to add your metadata
implementation to a GstBuffer
.
Your my-example-meta.h header file will
need thse additions:
[...] /* 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);
Let's have a look at how these functions are implemented in the my-example-meta.c file.
[...] 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 (g_once_init_enter (&meta_info)) { const GstMetaInfo *mi = gst_meta_register (MY_EXAMPLE_META_API_TYPE, "MyExampleMeta", sizeof (MyExampleMeta), my_example_meta_init, my_example_meta_free, my_example_meta_transform); g_once_init_leave (&meta_info, mi); } 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; }
gst_meta_register ()
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.
Lastly, you implement a gst_buffer_add_*_meta()
that adds the metadata implementation to a buffer and sets the
values of the metadata.