This sample application demonstrates how to create a simplified GStreamer pipeline for encoding and injecting MISB601 KLV metadata into STANAG 4609 files/streams using the MisbCore library
For the sake of simplicity, we'll use GStreamer's videotestsrc as a video source, encode it into H.264, encode a static metadata packet (updated with a current time ), multiplex everything, and record it into a standard-compliant STANAG 4609 file.
Building the pipeline
The code is pretty straightforward - we start manually creating a pipeline that consists of the videotestsrc and x264enc plugins, add multiplexer, and a filesink. Some additional auxiliary plugins will be added as well...
Walkthrough
Below we'll show some essential code snippets - everything else is a regular GStreamer code...
As with all GStreamer applications, we start by creating a pipeline and loading plugins.
We'll use appsrc as our data source, so let's configure it:
g_object_set(G_OBJECT(dataSrc), "caps", gst_caps_new_simple("meta/x-klv", "parsed", G_TYPE_BOOLEAN, TRUE, "spare", G_TYPE_BOOLEAN, TRUE, "is-live", G_TYPE_BOOLEAN, TRUE, NULL), NULL);
g_object_set(G_OBJECT(dataSrc), "format", GST_FORMAT_TIME, NULL);
g_object_set(G_OBJECT(dataSrc), "do-timestamp", TRUE, NULL);
We should also configure video encoding and file target parameters (check out the sample source code for more info).
Let's assign a callback need-data which will signal that the source needs more data. In this callback we'll encode our metadata and push the RAW KLV:
/* Assign callback to encode and push metadata */
g_signal_connect(dataSrc, "need-data", G_CALLBACK(pushKlv), NULL);
Before we implement the data injection, we need to load the misbCore library:
First, we load the library (somewhere at the beginning of our application):
void *handle;
/* Load library */
handle = dlopen((char *)PathToLibrary, RTLD_LAZY);
Next, we get the pointer to the encode method:
struct PcktBuffer
{
char* buffer;
int length;
};
typedef PcktBuffer (*encodeFunc)(char*);
encodeFunc encode601Pckt = (encodeFunc)funcAddr(handle, (char*)"Encode");
...
Now, lets look at the callback.
static void pushKlv(GstElement *src, guint, GstElement)
{
GstFlowReturn ret;
GstBuffer *buffer;
bool fInsert = false;
int len;
// Wait for a frame duration interval since the last inserted packet. As we're inserting ASYNC KLV, it is good enough for the demo...
while (!fInsert)
{
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
float diff = std::chrono::duration<float>(now - lastPcktTime).count();
if (diff > frameDuration)
fInsert = true;
else
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// First, encode the klv buffer from jsonPcktStr
// Encode json packet to data buffer
PcktBuffer pcktBuf = encode601Pckt((char*)(jspnPckt));
buffer = gst_buffer_new_allocate(NULL, pcktBuf.length, NULL);
// For ASYNC_KLV, we need to remove timestamp and duration from the buffer
GST_BUFFER_PTS(buffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DTS(buffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE;
// Fill the buffer with the encoded KLV data
gst_buffer_fill(pcktBuf.buffer, 0, buf, pcktBuf.length);
ret = gst_app_src_push_buffer((GstAppSrc *)src, buffer);
if (ret != GST_FLOW_OK)
{
g_printerr("Flow error");
g_main_loop_quit(loop);
}
else
{
lastPcktTime = std::chrono::steady_clock::now();
counter++;
g_print("Klv packet count: %llu. Buf size: %d \n", counter, len);
}
}
Klv timestamp
In the sample, we dont provide a mandatory Tag 2 (timestamp).
When no timestamp is found, MisbCore will automatically set the current time.
Running the application.
Now, everything is ready and we can run the application:
The NodeInfo: NI-MISBCORE-WCF9EAWJF46EC2EPQQXTFFDEH2NCB0KYXS1NBNHNSE0SB9K2KJAG:
Klv packet count: 1. Buf size: 106
Klv packet count: 2. Buf size: 106
Klv packet count: 3. Buf size: 106
Klv packet count: 4. Buf size: 106
Klv packet count: 5. Buf size: 106
Klv packet count: 6. Buf size: 106
Klv packet count: 7. Buf size: 106
Klv packet count: 8. Buf size: 106
Klv packet count: 9. Buf size: 106
STANAG 4609 file should be created and a packet counter with the buffer size will be sent to the console.
Cleaning up
MisbCore allocates memory for the last encoded buffer, so we need to free it at the end:
/* Clean up allocated resources */
cleanUpFunc cleanUp = (cleanUpFunc)funcAddr(handle, (char*)"CleanUp");
cleanUp();
Source code
The complete source code is available as part of SDK.
Adam
November 4, 2022 at 1:55 pmThanks for this article, i am very interested in to get the full version of the source code. At the end you mentioned that “The complete source code is available as part of SDK”, How do i can get this SDK? Do you have a free evaluation version?
Thanks,
-Adam
alexc
November 4, 2022 at 4:02 pmHi Adam,
Thanks for your interest in our products. Here is the source code (https://github.com/impleotv/gstreamer-test-klv).
You’ll need to download the MisbCore Native SDK (https://github.com/impleotv/misbcore-native-lib-release)
If you work with GStreamer you should probably check a better solution we have now:
GStreamer klv plugins (https://www.impleotv.com/content/gstreamer-klv-plugins/help/index.html). It is a native GStreamer solution, less code, etc.
Please note, there are bugs in GStreamer demux and mux. I’ve reported them on the GStreamer board:
– https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1504
– https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1307
So, you may need to use an older GStreamer version for this to work or apply the patch.
Thanks,
Alex