GstMeta

With the GstMeta system you can add arbitrary structures on buffers. These structures describe extra properties of the buffer such as cropping, stride, region of interest etc.

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.

GstMeta API example

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);
  }
[...]


      

Implementing new GstMeta

In the next sections we show how you can add new metadata to the system and use it on buffers.

Define the metadata API

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.

Implementing a metadata 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.