GStreamer In-Band Metadata for MPEG Transport Stream - Examples - C - C++
GStreamer In-Band Metadata for MPEG Transport Stream |
---|
![]() |
MPEG TS Metadata Basics |
Getting Started |
User Guide |
Examples |
FAQ |
Contact Us |
![]() | Evaluation binary should be installed first to run any of the example programs in this guide. |
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
metasrc = gst_bin_get_by_name(GST_BIN(pipeline), "meta"); if (!metasrc) { g_printerr("Could not get metadata 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_metadta, metalen); barray = g_byte_array_new_take(array_copy, metalen); metalen = sizeof(klv_metadata); barray = g_byte_array_new_take(klv_metadata, 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);
- 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.
![]() | NOTE:Metadata can also be injected via an action signal, see the metadata demo application included with the plugin for a usage example. |
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.