GstSEIMetadata - Examples - C Example

From RidgeRun Developer Wiki


Previous: Examples/Using Gstd Index Next: Examples/Latency Measurement Example




Inserting metadata as binary using a C application

The following is an example that shows how to insert metadata as binary through a C application.

/**
  * Copyright (C) 2021 RidgeRun, LLC (http://www.ridgerun.com)
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License version 2 as
  * published by the Free Software Foundation.
  */

#include <glib.h>
#include <glib-unix.h>
#include <gst/gst.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PIPELINE_DESCRIPTION                                                   \
  "videotestsrc num-buffers=10 ! video/x-raw,width=16,height=240 ! x264enc ! video/x-h264,stream-format=avc ! "      \
  "seiinject name=inject ! filesink name=sink location=seiinject_metadata_binary_videotestsrc_avc.h264"

#define FILE_LOCATION "./seiinject_metadata_binary_videotestsrc_avc.h264"

static guint8 binary_metadata[] = {
    /*This is "Hello World!!" in hexadecimal*/
    0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x21,
};

typedef struct _GstMetaDemo GstMetaDemo;
struct _GstMetaDemo {
  GstElement *pipeline;
  GstElement *seiinject;
  GstElement *filesink;
  GMainLoop *main_loop;
};

static gboolean create_pipeline(GstMetaDemo *metademo);
static void start_pipeline(GstMetaDemo *metademo);
static void stop_pipeline(GstMetaDemo *metademo);
static void release_resources(GstMetaDemo *metademo);
static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data);

int main(int argc, char *argv[]) {

  GstMetaDemo *metademo = g_malloc(sizeof(GstMetaDemo));
  if(!metademo){
    g_print("Could not create demo\n");
    return 1;
  }

  /* Initialization */
  gst_init(&argc, &argv);

  if (!create_pipeline(metademo)) {
    g_free(metademo);
    return 1;
  }

  /* Set the pipeline to "playing" state*/
  g_print("Playing pipeline\n");
  start_pipeline(metademo);

  /* Iterate */
  g_print("Running...\n");
  g_main_loop_run(metademo->main_loop);

  /* Out of the main loop, clean up nicely */
  g_print("Returned, stopping playback\n");
  release_resources(metademo);

  return 0;
}

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
  GMainLoop *loop = (GMainLoop *)data;

  switch (GST_MESSAGE_TYPE(msg)) {

  case GST_MESSAGE_EOS:
    g_print("End of stream\n");
    g_main_loop_quit(loop);
    break;

  case GST_MESSAGE_ERROR: {
    gchar *debug;
    GError *error;

    gst_message_parse_error(msg, &error, &debug);
    g_free(debug);

    g_printerr("Error: %s\n", error->message);
    g_error_free(error);

    g_main_loop_quit(loop);
    break;
  }
  default:
    break;
  }

  return TRUE;
}

static gboolean create_pipeline(GstMetaDemo *metademo) {

  GMainLoop *loop;

  GstElement *pipeline = NULL;
  GstElement *seiinject = NULL;
  GstElement *filesink = NULL;
  GstBus *bus = NULL;
  GByteArray *barray = NULL;
  guint metalen = 0;
  GError *error = NULL;

  if (!metademo) {
    return FALSE;
  }

  loop = g_main_loop_new(NULL, FALSE);

  pipeline = gst_parse_launch(PIPELINE_DESCRIPTION, &error);

  if (error) {
    g_printerr("Unable to build pipeline (%s)\n", error->message);
    g_clear_error(&error);
    return FALSE;
  }

  seiinject = gst_bin_get_by_name(GST_BIN(pipeline), "inject");
  if (!seiinject) {
    g_printerr("Could not get metadata element\n");
    return FALSE;
  }

  filesink = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
  if (!filesink) {
    g_printerr("Could not get filesink element\n");
    return FALSE;
  }

  /*Prepare metadata*/
  metalen = sizeof(binary_metadata);
  barray = g_byte_array_new_take(binary_metadata, metalen);
  g_object_set(seiinject, "metadata-binary", barray, NULL);
  g_boxed_free(G_TYPE_BYTE_ARRAY, barray);

  g_object_set(G_OBJECT(filesink), "location", FILE_LOCATION, NULL);

  /* we add a message handler */
  bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
  gst_bus_add_watch(bus, bus_call, loop);
  gst_object_unref(bus);

  metademo->pipeline = pipeline;
  metademo->main_loop = loop;
  metademo->seiinject = seiinject;
  metademo->filesink = filesink;

  return TRUE;
}

static void start_pipeline(GstMetaDemo *metademo) {
  gst_element_set_state(metademo->pipeline, GST_STATE_PLAYING);
}
static void stop_pipeline(GstMetaDemo *metademo) {
  gst_element_set_state(metademo->pipeline, GST_STATE_NULL);
}

static void release_resources(GstMetaDemo *metademo) {
  if (!metademo) {
    return;
  }

  stop_pipeline(metademo);

  if (metademo->pipeline) {
    gst_object_unref(metademo->pipeline);
    metademo->pipeline = NULL;
  }

  if (metademo->seiinject) {
    gst_object_unref(metademo->seiinject);
    metademo->seiinject = NULL;
  }

  if (metademo->filesink) {
    gst_object_unref(metademo->filesink);
    metademo->filesink = NULL;
  }

  if (metademo->main_loop) {
    g_main_loop_unref(metademo->main_loop);
    metademo->main_loop = NULL;
  }
}

static gboolean handle_signal(gpointer data) {
  GstMetaDemo *metademo = (GstMetaDemo *)data;

  g_main_loop_quit(metademo->main_loop);

  return TRUE;
}

Application Walk-through

This is a quick walk-trough to better understand the process of creating the pipeline and injecting the metadata in the previous example.

Pipeline description

#define PIPELINE_DESCRIPTION                                                   \
  "videotestsrc num-buffers=10 ! video/x-raw,width=16,height=240 ! x264enc ! video/x-h264,stream-format=avc ! "      \
  "seiinject name=inject ! filesink name=sink location=seiinject_metadata_binary_videotestsrc_avc.h264"

As seen above, all we need to inject the metadata is the seiinject element, which will take the provided metadata, inject it into the encoded stream and then store it in the file seiinject_metadata_binary_videotestsrc_avc.h264

Metadata packet


static guint8 binary_metadata[] = {
    /*This is "Hello World!!" in hexadecimal*/
    0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x21,
};

Note that this could have been any array of bytes, the above one is just an example.

Metadata injection

  seiinject = gst_bin_get_by_name(GST_BIN(pipeline), "inject");
  if (!seiinject) {
    g_printerr("Could not get metadata element\n");
    return FALSE;
  }

  filesink = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
  if (!filesink) {
    g_printerr("Could not get filesink element\n");
    return FALSE;
  }

  /*Prepare metadata*/
  metalen = sizeof(binary_metadata);
  barray = g_byte_array_new_take(binary_metadata, metalen);
  g_object_set(seiinject, "metadata-binary", barray, NULL);
  g_boxed_free(G_TYPE_BYTE_ARRAY, barray);

  g_object_set(G_OBJECT(filesink), "location", FILE_LOCATION, NULL);
  • metadata binary: In line 16, the metadata binary is injected using the metadata-binary property.

Building the application

In order to build the appication, copy the example into a file called main.c and build using the following command:

gcc -Wall main.c -o main `pkg-config --cflags --libs gstreamer-1.0`

After this you will end up with a binary called main.

Running the application

Execute the application with the following command:

GST_DEBUG="2,*obu*:MEMDUMP" ./main

This will create a file called seiinject_metadata_binary_videotestsrc_avc.h264 in the same directory where the application is running.

To check the data was inserted correctly you can run:

GST_DEBUG=*seiextract*:MEMDUMP gst-launch-1.0 filesrc location=seiinject_metadata_binary_videotestsrc_avc.h264 ! video/x-h264,stream-format=avc ! seiextract ! fakesink

And you will see something like this:

Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
0:00:00.017574482  7940 0x55ba08c160a0 DEBUG             seiextract gstseiextract.c:251:gst_sei_extract_set_caps:<seiextract0> set_caps
0:00:00.017615098  7940 0x55ba08c160a0 DEBUG             seiextract gstseiextract.c:257:gst_sei_extract_set_caps: parsing caps: video/x-h264, stream-format=(string)avc
0:00:00.017748013  7940 0x55ba08c160a0 LOG               seiextract gstseiextract.c:295:gst_sei_extract_prepare_output_buffer:<seiextract0> prepare_output_buffer
0:00:00.017781023  7940 0x55ba08c160a0 LOG               seiextract gstseiextract.c:345:gst_sei_extract_prepare_output_buffer:<seiextract0> Not the expected payload type: 5

0:00:00.017789475  7940 0x55ba08c160a0 LOG               seiextract gstseiextract.c:352:gst_sei_extract_prepare_output_buffer:<seiextract0> The extracted data is: Hello World!!
...

Where you can see the "Hello World!!" message we inserted as binary.


Previous: Examples/Using Gstd Index Next: Examples/Latency Measurement Example