Difference between revisions of "GStreamer In-Band Metadata for MPEG Transport Stream/Examples/ C/C++"
(→Metadata injection) |
|||
Line 305: | Line 305: | ||
=== Metadata injection === | === Metadata injection === | ||
− | <syntaxhighlight lang="C" line highlight="12, | + | <syntaxhighlight lang="C" line highlight="12-14,20"> |
metasrc = gst_bin_get_by_name(GST_BIN(pipeline), "meta"); | metasrc = gst_bin_get_by_name(GST_BIN(pipeline), "meta"); | ||
if (!metasrc) { | if (!metasrc) { | ||
Line 331: | Line 331: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | * '''metadata binary:''' In line | + | * '''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. | * '''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. | + | '''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 == | == Building the application == |
Revision as of 15:38, 21 February 2022
GStreamer In-Band Metadata for MPEG Transport Stream | |
---|---|
MPEG TS Metadata Basics | |
|
|
Getting Started | |
|
|
User Guide | |
|
|
Examples | |
|
|
FAQ | |
Contact Us |
Contents
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_metadta, 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.
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.