GStreamer In-Band Metadata for MPEG Transport Stream - Examples - C - C++

From RidgeRun Developer Connection
Jump to: navigation, search


Previous: Examples/Gstd Index Next: FAQ






Sample application

In the following example, we show you how to use Gstreamer In-Band metadata to add a KLV packet into a Transport Stream.

/**
  * Copyright (C) 2020 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                                                   \
  "metasrc name=meta ! meta/x-klv,stream_type=21 ! mpegtsmux name=mux ! "      \
  "filesink name=sink location=/tmp/metadata.ts videotestsrc pattern=18 "      \
  "is-live=true ! video/x-raw,width=640,height=480,framerate=30/1 ! queue ! "  \
  "x264enc ! mux."

#define METADATA_PERIOD_SECS 1
#define FILE_LOCATION "./metadata.ts"

/*
KLV taken from:
https://www.researchgate.net/figure/Example-of-a-metadata-Key-Length-Value-KLV-packet-It-is-formed-by-a-key-in-green_fig3_313416345

Ruano, Susana & Cuevas, Carlos & Gallego, Guillermo & García, Narciso. (2017). 
Augmented Reality Tool for the Situational Awareness Improvement of UAV Operators. Sensors. 
17. 297. 10.3390/s17020297. 
*/
static guint8 klv_metadata[] = {
    /*Sample KLV metadata*/
    0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x01,
    0x01, 0x00, 0x00, 0x00, 0x81, 0xAE, 0x02, 0x08, 0x00, 0x04, 0x60, 0x50,
    0x58, 0x4E, 0x01, 0x80, 0x03, 0x0A, 0x4D, 0x69, 0x73, 0x73, 0x69, 0x6F,
    0x6E, 0x20, 0x31, 0x32, 0x05, 0x02, 0x71, 0xC2, 0x06, 0x02, 0xFD, 0x3D,
    0x07, 0x02, 0x08, 0xB8, 0x0A, 0x08, 0x50, 0x72, 0x65, 0x64, 0x61, 0x74,
    0x6F, 0x72, 0x0B, 0x07, 0x45, 0x4F, 0x20, 0x4E, 0x6F, 0x73, 0x65, 0x0C,
    0x0E, 0x47, 0x65, 0x6F, 0x64, 0x65, 0x74, 0x69, 0x63, 0x20, 0x57, 0x47,
    0x53, 0x38, 0x34, 0x0D, 0x04, 0x55, 0x95, 0xB6, 0x6D, 0x0E, 0x04, 0x5B,
    0x53, 0x60, 0xC4, 0x0F, 0x02, 0xC2, 0x21, 0x10, 0x02, 0xCD, 0x9C, 0x11,
    0x02, 0xD9, 0x17, 0x12, 0x04, 0x72, 0x4A, 0x0A, 0x20, 0x13, 0x04, 0x87,
    0xF8, 0x4B, 0x86, 0x14, 0x04, 0x00, 0x00, 0x00, 0x00, 0x15, 0x04, 0x03,
    0x83, 0x09, 0x26, 0x16, 0x02, 0x12, 0x81, 0x17, 0x04, 0xF1, 0x01, 0xA2,
    0x29, 0x18, 0x04, 0x14, 0xBC, 0x08, 0x2B, 0x19, 0x02, 0x34, 0xF3, 0x30,
    0x1C, 0x01, 0x01, 0x01, 0x02, 0x01, 0x07, 0x03, 0x05, 0x2F, 0x2F, 0x55,
    0x53, 0x41, 0x0C, 0x01, 0x07, 0x0D, 0x06, 0x00, 0x55, 0x00, 0x53, 0x00,
    0x41, 0x16, 0x02, 0x04, 0x01, 0x41, 0x01, 0x02, 0x01, 0x02, 0x29, 0x72,
};

typedef struct _GstMetaDemo GstMetaDemo;
struct _GstMetaDemo {
  GstElement *pipeline;
  GstElement *metasrc;
  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);
static gboolean handle_signal(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;
  }

  g_unix_signal_add(SIGINT, (GSourceFunc)handle_signal, metademo);

  /* 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 *metasrc = NULL;
  GstElement *filesink = NULL;
  GstBus *bus = NULL;
  GByteArray *barray = NULL;
  guint8 *array_copy = 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;
  }

  metasrc = gst_bin_get_by_name(GST_BIN(pipeline), "meta");
  if (!metasrc) {
    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*/

  /*We need to copy the array since the GByteArray will be the new owner and free it for us*/
  metalen = sizeof(klv_metadata);
  array_copy = g_malloc (metalen);
  memcpy (array_copy, klv_metadata, metalen);
  
  barray = g_byte_array_new_take(array_copy, metalen);
  g_object_set(metasrc, "metadata-binary", barray, NULL);
  g_boxed_free(G_TYPE_BYTE_ARRAY, barray);

  g_object_set(G_OBJECT(metasrc), "period", METADATA_PERIOD_SECS, NULL);
  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->metasrc = metasrc;
  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->metasrc) {
    gst_object_unref(metademo->metasrc);
    metademo->metasrc = 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                                                   \
  "metasrc name=meta ! meta/x-klv,stream_type=21 ! mpegtsmux name=mux ! "      \
  "filesink name=sink location=/tmp/metadata.ts videotestsrc pattern=18 "      \
  "is-live=true ! video/x-raw,width=640,height=480,framerate=30/1 ! queue ! "  \
  "x264enc ! mux."

As seen above, all we need to inject the metadata is the metasrc element, which will take the provided metadata, pack it in a Gstreamer Buffer and send it downstream to me muxed by mpegtsmux.

Metadata packet

/*
KLV taken from:
https://www.researchgate.net/figure/Example-of-a-metadata-Key-Length-Value-KLV-packet-It-is-formed-by-a-key-in-green_fig3_313416345

Ruano, Susana & Cuevas, Carlos & Gallego, Guillermo & García, Narciso. (2017). 
Augmented Reality Tool for the Situational Awareness Improvement of UAV Operators. Sensors. 
17. 297. 10.3390/s17020297. 
*/
static guint8 klv_metadata[] = {
    /*Sample KLV metadata*/
    0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x01,
    0x01, 0x00, 0x00, 0x00, 0x81, 0xAE, 0x02, 0x08, 0x00, 0x04, 0x60, 0x50,
    0x58, 0x4E, 0x01, 0x80, 0x03, 0x0A, 0x4D, 0x69, 0x73, 0x73, 0x69, 0x6F,
    0x6E, 0x20, 0x31, 0x32, 0x05, 0x02, 0x71, 0xC2, 0x06, 0x02, 0xFD, 0x3D,
    0x07, 0x02, 0x08, 0xB8, 0x0A, 0x08, 0x50, 0x72, 0x65, 0x64, 0x61, 0x74,
    0x6F, 0x72, 0x0B, 0x07, 0x45, 0x4F, 0x20, 0x4E, 0x6F, 0x73, 0x65, 0x0C,
    0x0E, 0x47, 0x65, 0x6F, 0x64, 0x65, 0x74, 0x69, 0x63, 0x20, 0x57, 0x47,
    0x53, 0x38, 0x34, 0x0D, 0x04, 0x55, 0x95, 0xB6, 0x6D, 0x0E, 0x04, 0x5B,
    0x53, 0x60, 0xC4, 0x0F, 0x02, 0xC2, 0x21, 0x10, 0x02, 0xCD, 0x9C, 0x11,
    0x02, 0xD9, 0x17, 0x12, 0x04, 0x72, 0x4A, 0x0A, 0x20, 0x13, 0x04, 0x87,
    0xF8, 0x4B, 0x86, 0x14, 0x04, 0x00, 0x00, 0x00, 0x00, 0x15, 0x04, 0x03,
    0x83, 0x09, 0x26, 0x16, 0x02, 0x12, 0x81, 0x17, 0x04, 0xF1, 0x01, 0xA2,
    0x29, 0x18, 0x04, 0x14, 0xBC, 0x08, 0x2B, 0x19, 0x02, 0x34, 0xF3, 0x30,
    0x1C, 0x01, 0x01, 0x01, 0x02, 0x01, 0x07, 0x03, 0x05, 0x2F, 0x2F, 0x55,
    0x53, 0x41, 0x0C, 0x01, 0x07, 0x0D, 0x06, 0x00, 0x55, 0x00, 0x53, 0x00,
    0x41, 0x16, 0x02, 0x04, 0x01, 0x41, 0x01, 0x02, 0x01, 0x02, 0x29, 0x72,
};

Here we are using a sample KLB packet (MISB ST0601), however the metasrc element allows the injection of metadata with any format by using the follwing properties:

  • metadata: A string to push as metadata. This string may contain time formats as specified in C99, specifically the ones supported by g_date_time_format(). An update in this property will affect the "metadata-binary" value.
  • metadata-binary: A binary blob to push as metadata. An update in this property will clear up the "metadata" value.

Metadata injection

 1  metasrc = gst_bin_get_by_name(GST_BIN(pipeline), "meta");
 2   if (!metasrc) {
 3     g_printerr("Could not get metadata element\n");
 4     return FALSE;
 5   }
 6 
 7 ...
 8 
 9   /*Prepare metadata*/
10 
11   /*We need to copy the array since the GByteArray will be the new owner and free it for us*/
12   metalen = sizeof(klv_metadata);
13   array_copy = g_malloc (metalen);
14   memcpy (array_copy, klv_metadta, metalen);
15   
16   barray = g_byte_array_new_take(array_copy, metalen);
17 
18   metalen = sizeof(klv_metadata);
19   barray = g_byte_array_new_take(klv_metadata, metalen);
20   g_object_set(metasrc, "metadata-binary", barray, NULL);
21   g_boxed_free(G_TYPE_BYTE_ARRAY, barray);
22 
23   g_object_set(G_OBJECT(metasrc), "period", METADATA_PERIOD_SECS, NULL);
  • metadata copy: From line2 12-14 we make a copy of the array, since we give ownership of the data to GByteArray. The GByteArray will free this data using g_free automatically. If you are okay transferring the ownership, you may remove this copy.
  • metadata binary: In line 20, the metadata binary is injected using the metadata-binary property.
  • metadata period: The period of the metadata is controlled with the period property. In this example we are using 1 second but if no value is specified then the metadata will be added at the moment the metadata or metadata-binary properties are updated.

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:

./main

This will create a file called 'metadata.ts in the same directory where the application is running.

Stop the application with Ctrl + C.


Previous: Examples/Gstd Index Next: FAQ