Jump to content

GstExtractor: Difference between revisions

m
no edit summary
mNo edit summary
mNo edit summary
Line 23: Line 23:
It is the responsibility of the user to implement an appropriate customized parser and add the parser to the GstExtractor.  The parser generates the binary SEI and KLV encoded data.
It is the responsibility of the user to implement an appropriate customized parser and add the parser to the GstExtractor.  The parser generates the binary SEI and KLV encoded data.


{{Ambox
{{Colored box|background-content-color=#EDF1F7|background-title-color=#6586B9|title-color=#FFFFFF|title='''Note'''|icon=notice-icon-white.png
|type=notice
  |style=overflow:hidden;
|small=left
  |content=
|issue='''Note''': In order to use the SEI metadata feature, you need to also acquire RidgeRun's [https://developer.ridgerun.com/wiki/index.php?title=GstSEIMetadata GstSEIMetadata plugin].
  In order to use the SEI metadata feature, you need to also acquire RidgeRun's [[GstSEIMetadata | GstSEIMetadata plugin]].
|style=width:unset;
}}
}}
== Getting the Code ==
== Getting the Code ==


When you purchase GstExtractor, you will get a git repository with the source code inside. You need to build the plug-in in your system (since you are adding a customized parser) then add the plug-in to your GStreamer catalogue. Clone the repository using something similar to:
When you purchase GstExtractor, you will get a git repository with the source code inside. You need to build the plug-in in your system (since you are adding a customized parser) then add the plug-in to your GStreamer catalogue. Clone the repository using something similar to:


<source lang="bash">
<syntaxhighlight lang="bash">
git clone git://git@gitlab.com/RidgeRun/orders/${CUSTOMER_DIRECTORY}/gst-extractor.git
git clone git://git@gitlab.com/RidgeRun/orders/${CUSTOMER_DIRECTORY}/gst-extractor.git
cd gst-extractor
cd gst-extractor
GST_EXTRACTOR_DIR=${PWD}
GST_EXTRACTOR_DIR=${PWD}
</source>
</syntaxhighlight>


Where <code>${CUSTOMER_DIRECTORY}</code> contains the name of your customer directory given by RidgeRun after purchase.
Where <code>${CUSTOMER_DIRECTORY}</code> contains the name of your customer directory given by RidgeRun after purchase.
Line 92: Line 90:
* The '''filepath''' property is optional in case you want to show a dummy image if a given condition is met. This image should be raw (not encoded) and of the same format and size of the normal stream.  The custom embedded data parser is responsible for deciding (based on the contents of the embedded data) if the actual frame, a dummy image or if no frame at all (dropping) should go downstream. An example use is if the embedded data includes a flag which indicates if the frame data is empty due to no video signal being received.  
* The '''filepath''' property is optional in case you want to show a dummy image if a given condition is met. This image should be raw (not encoded) and of the same format and size of the normal stream.  The custom embedded data parser is responsible for deciding (based on the contents of the embedded data) if the actual frame, a dummy image or if no frame at all (dropping) should go downstream. An example use is if the embedded data includes a flag which indicates if the frame data is empty due to no video signal being received.  


{{Ambox
{{Colored box|background-content-color=#EDF1F7|background-title-color=#6586B9|title-color=#FFFFFF|title='''Note'''|icon=notice-icon-white.png
|type=notice
  |style=overflow:hidden;
|small=left
  |content=
|issue='''Note''': Output caps will be set based on '''height-padding''', so this property value will only be valid during caps negotiation and further writes to it won't have an effect.
  Output caps will be set based on '''height-padding''', so this property value will only be valid during caps negotiation and further writes to it won't have an effect.
|style=width:unset;
}}
}}
=== Custom Data Parsing Implementation ===
=== Custom Data Parsing Implementation ===


GstExtractor will be in charge of separating the embedded data from the video frame and handling the embedded data appropriately, such as sending the data as H264/5 SEI metadata and KLV metadata buffer.  GstExtractor includes helper functions to make it easy for the custom embedded parser to send SEI and KLV data downstream as shown in Figure 2. In order to make your custom embedded data parser, GstExtractor contains a special code section in the file ''gst/gstextractor.c'' for you to modify. This section currently has a basic example of how this could be done, but you must modify it according to your needs:
GstExtractor will be in charge of separating the embedded data from the video frame and handling the embedded data appropriately, such as sending the data as H264/5 SEI metadata and KLV metadata buffer.  GstExtractor includes helper functions to make it easy for the custom embedded parser to send SEI and KLV data downstream as shown in Figure 2. In order to make your custom embedded data parser, GstExtractor contains a special code section in the file ''gst/gstextractor.c'' for you to modify. This section currently has a basic example of how this could be done, but you must modify it according to your needs:


<source lang=c>
<syntaxhighlight lang="C">
   {
   {
     /* TODO: Add your custom parsing here. This is just an example that
     /* TODO: Add your custom parsing here. This is just an example that
Line 146: Line 142:
     GST_OBJECT_UNLOCK (self);
     GST_OBJECT_UNLOCK (self);
   }
   }
</source>
</syntaxhighlight>


==== Example Data Parsing Implementation Walkthrough ====
==== Example Data Parsing Implementation Walkthrough ====
Line 152: Line 148:
1. First we declare and assign some variables for the parsing:
1. First we declare and assign some variables for the parsing:


<source lang=c>
<syntaxhighlight lang="C">
const gint bin_size = 5;
const gint bin_size = 5;
const gint bin_threshold = 50;
const gint bin_threshold = 50;
Line 158: Line 154:
sei_size = 20;
sei_size = 20;
klv_size = 14;
klv_size = 14;
</source>
</syntaxhighlight>


It's mandatory that you assign the '''sei_size''' and '''klv_size''' to their corresponding value, otherwise they will be set to 0 and the further buffer/metadata creation will fail. '''bin_size''' and '''bin_threshold''' on the other side, are local variables for the parsing to set the ''value'' property and to replace the input buffer with a dummy frame respectively, so they are optional. '''drop_probability''' is optional as well, and it's only to show how you could configure the parser also to drop a frame and/or meta.
It's mandatory that you assign the '''sei_size''' and '''klv_size''' to their corresponding value, otherwise they will be set to 0 and the further buffer/metadata creation will fail. '''bin_size''' and '''bin_threshold''' on the other side, are local variables for the parsing to set the ''value'' property and to replace the input buffer with a dummy frame respectively, so they are optional. '''drop_probability''' is optional as well, and it's only to show how you could configure the parser also to drop a frame and/or meta.
Line 164: Line 160:
2. "Parse" and assign the result to the provided pointers:
2. "Parse" and assign the result to the provided pointers:


<source lang=c>
<syntaxhighlight lang="C">
sei_data = (guint8 *) g_memdup (data, sei_size);
sei_data = (guint8 *) g_memdup (data, sei_size);
klv_data = (guint8 *) g_memdup (data + sei_size, klv_size);
klv_data = (guint8 *) g_memdup (data + sei_size, klv_size);
</source>
</syntaxhighlight>


In this example we are not really parsing anything, just copying the data into the '''sei_data''' and '''klv_data''' pointers, but this should be the last step after you parse/encode your data. Keep in mind that '''sei_data''' and '''klv_data''' are null pointers at that point, so you need to allocate them with '''sei_size''' and '''klv_size''' respectively (in this case the ''g_memdup'' function takes care of it).
In this example we are not really parsing anything, just copying the data into the '''sei_data''' and '''klv_data''' pointers, but this should be the last step after you parse/encode your data. Keep in mind that '''sei_data''' and '''klv_data''' are null pointers at that point, so you need to allocate them with '''sei_size''' and '''klv_size''' respectively (in this case the ''g_memdup'' function takes care of it).
Line 175: Line 171:
3. Lock the object since we will modify data that can be accessed from another thread:
3. Lock the object since we will modify data that can be accessed from another thread:


<source lang=c>
<syntaxhighlight lang="C">
GST_OBJECT_LOCK (self);
GST_OBJECT_LOCK (self);
</source>
</syntaxhighlight>


4. Assign the appropriate binary data to the '''value''' property:
4. Assign the appropriate binary data to the '''value''' property:


<source lang=c>
<syntaxhighlight lang="C">
if (self->value) {
if (self->value) {
   g_byte_array_free (self->value, TRUE);
   g_byte_array_free (self->value, TRUE);
Line 188: Line 184:
self->value->len = bin_size;
self->value->len = bin_size;
memcpy (self->value->data, klv_data + (klv_size - bin_size), bin_size);
memcpy (self->value->data, klv_data + (klv_size - bin_size), bin_size);
</source>
</syntaxhighlight>


If you don't need this step, just remove this code.
If you don't need this step, just remove this code.


5. Determine if the frame and/or meta should be dropped
5. Determine if the frame and/or meta should be dropped
<source lang=c>
<syntaxhighlight lang="C">
     drop_frame = ((gfloat) (1.0 * rand () / (RAND_MAX)) < drop_probability);
     drop_frame = ((gfloat) (1.0 * rand () / (RAND_MAX)) < drop_probability);
     drop_meta = drop_frame;
     drop_meta = drop_frame;
</source>
</syntaxhighlight>


In this example we are just setting a probability for dropping the frame to show how the feature works but you can omit it if you don't need it.
In this example we are just setting a probability for dropping the frame to show how the feature works but you can omit it if you don't need it.
Line 202: Line 198:
6. Create a proper custom condition to know when to replace the input buffer with the dummy frame. You should only replace the ''(atoi ((char *) self->value->data) < bin_threshold)'' condition. Keep in mind that for this piece of code to work you need to assign the '''filepath''' property to extractor element.
6. Create a proper custom condition to know when to replace the input buffer with the dummy frame. You should only replace the ''(atoi ((char *) self->value->data) < bin_threshold)'' condition. Keep in mind that for this piece of code to work you need to assign the '''filepath''' property to extractor element.


<source lang=bash>
<syntaxhighlight lang="bash">
if ((atoi ((char *) self->value->data) < bin_threshold) && self->filepath /* Replace with custom condition */
if ((atoi ((char *) self->value->data) < bin_threshold) && self->filepath /* Replace with custom condition */
         && !drop_frame) {  
         && !drop_frame) {  
Line 212: Line 208:
       inbuf = gst_buffer_make_writable (inbuf);
       inbuf = gst_buffer_make_writable (inbuf);
}
}
</source>
</syntaxhighlight>


This custom condition includes the ''drop_frame'' flag verification so if you set it to TRUE, it won't use the dummy buffer since it would be useless. If you don't need this step, just remove this code.
This custom condition includes the ''drop_frame'' flag verification so if you set it to TRUE, it won't use the dummy buffer since it would be useless. If you don't need this step, just remove this code.
Line 218: Line 214:
7. Unlock the object.
7. Unlock the object.


<source lang=c>
<syntaxhighlight lang="C">
GST_OBJECT_UNLOCK (self);
GST_OBJECT_UNLOCK (self);
</source>
</syntaxhighlight>


8. Follow the next section to install and test your changes.
8. Follow the next section to install and test your changes.
Line 237: Line 233:
In order to install the first three run the following commands:
In order to install the first three run the following commands:


<source lang=bash>
<syntaxhighlight lang="bash">
# GStreamer
# GStreamer
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base
Line 243: Line 239:
sudo apt install python3 python3-pip python3-setuptools python3-wheel ninja-build
sudo apt install python3 python3-pip python3-setuptools python3-wheel ninja-build
sudo -H pip3 install git+https://github.com/mesonbuild/meson.git
sudo -H pip3 install git+https://github.com/mesonbuild/meson.git
</source>
</syntaxhighlight>


Then, GstSEIMetadata is a proprietary product from RidgeRun, so check this [https://developer.ridgerun.com/wiki/index.php?title=GstSEIMetadata link] to learn how to get and install it.
Then, GstSEIMetadata is a proprietary product from RidgeRun, so check this [[GstSEIMetadata | link]] to learn how to get and install it.


=== Compile and Install ===
=== Compile and Install ===
Line 261: Line 257:
Then, run:
Then, run:


<source lang=bash>
<syntaxhighlight lang="bash">
cd $GST_EXTRACTOR_DIR # Path to your gst-extractor cloned repository
cd $GST_EXTRACTOR_DIR # Path to your gst-extractor cloned repository
meson build --prefix /usr --libdir $LIBDIR
meson build --prefix /usr --libdir $LIBDIR
ninja -C build
ninja -C build
sudo ninja -C build install
sudo ninja -C build install
</source>
</syntaxhighlight>


== Examples ==
== Examples ==
Line 325: Line 321:
gst-launch-1.0 filesrc location=${INPUT} ! videoparse width=1280 height=722 format=yuy2 framerate=30 ! extractor data-size=34 height-padding=2 name=ext ext.video ! videorate ! video/x-raw,framerate=30/1 ! queue ! videoconvert ! ximagesink sync=false ext.klv ! queue ! filesink location=meta.klv
gst-launch-1.0 filesrc location=${INPUT} ! videoparse width=1280 height=722 format=yuy2 framerate=30 ! extractor data-size=34 height-padding=2 name=ext ext.video ! videorate ! video/x-raw,framerate=30/1 ! queue ! videoconvert ! ximagesink sync=false ext.klv ! queue ! filesink location=meta.klv
</pre>
</pre>


=== Advanced Examples ===
=== Advanced Examples ===
Line 357: Line 352:
</pre>
</pre>


Note that to use this pipeline you should have installed GstSEI on the receiving side too. The debug flags ''GST_DEBUG=2,*seiextract*:MEMDUMP'' will allow you to see the extracted SEI metadata into the GStreamer debug. Also, if you would like to see the KLV metadata in the terminal output too, you can check our [https://developer.ridgerun.com/wiki/index.php?title=GStreamer_In-Band_Metadata_for_MPEG_Transport_Stream GStreamer In-Band Metadata for MPEG Transport Stream plugin] and replace the '''fakesink''' element with '''metasink'''. Then you should see something similar to this on the receiving side:
Note that to use this pipeline you should have installed GstSEI on the receiving side too. The debug flags ''GST_DEBUG=2,*seiextract*:MEMDUMP'' will allow you to see the extracted SEI metadata into the GStreamer debug. Also, if you would like to see the KLV metadata in the terminal output too, you can check our [[GStreamer_In-Band_Metadata_for_MPEG_Transport_Stream | GStreamer In-Band Metadata for MPEG Transport Stream plugin]] and replace the '''fakesink''' element with '''metasink'''. Then you should see something similar to this on the receiving side:


<source lang=bash>
<syntaxhighlight lang="bash">
# KLV meta
# KLV meta
00000000 (0x7f893402e590): 73 6f 6d 65 20 6b 6c 76 20 30 31 34 39 00        some klv 0149.   
00000000 (0x7f893402e590): 73 6f 6d 65 20 6b 6c 76 20 30 31 34 39 00        some klv 0149.   
Line 369: Line 364:
0:00:43.188620166 146210 0x55b6049b0f60 MEMDUMP          seiextract gstseiextract.c:356:gst_sei_extract_extract_h265_data:<seiextract0> 00000010: 3a 30 30 20                                      :00             
0:00:43.188620166 146210 0x55b6049b0f60 MEMDUMP          seiextract gstseiextract.c:356:gst_sei_extract_extract_h265_data:<seiextract0> 00000010: 3a 30 30 20                                      :00             
0:00:43.188650823 146210 0x55b6049b0f60 MEMDUMP          seiextract gstseiextract.c:356:gst_sei_extract_extract_h265_data:<seiextract0> ---------------------------------------------------------------------------
0:00:43.188650823 146210 0x55b6049b0f60 MEMDUMP          seiextract gstseiextract.c:356:gst_sei_extract_extract_h265_data:<seiextract0> ---------------------------------------------------------------------------
</source>
</syntaxhighlight>


Where the injected metadata was '''some klv 0149''' and '''05-11-2021-15:43:00''' for KLV and SEI, respectively.
Where the injected metadata was '''some klv 0149''' and '''05-11-2021-15:43:00''' for KLV and SEI, respectively.
Line 378: Line 373:


* '''stream-recovered''': Posted to the bus when it pushes a frame downstream after having dropped one or more.
* '''stream-recovered''': Posted to the bus when it pushes a frame downstream after having dropped one or more.
<source lang=bash>
<syntaxhighlight lang="bash">
"stream-recovered" : {
"stream-recovered" : {
     }
     }
</source>
</syntaxhighlight>


* '''stream-lost''': Posted to the bus every time a video frame is dropped, it increases the '''drop-count''' accordingly.
* '''stream-lost''': Posted to the bus every time a video frame is dropped, it increases the '''drop-count''' accordingly.
<source lang=bash>
<syntaxhighlight lang="bash">
"stream-lost" : {
"stream-lost" : {
         "drop-count" : 49
         "drop-count" : 49
     }
     }
</source>
</syntaxhighlight>


So in the next example with the help of those messages we will query the amount of dropped frames and if it goes above a threshold we will switch the input stream to a dummy stream that will be playing a file on loop with a message of ''Signal Lost''.
So in the next example with the help of those messages we will query the amount of dropped frames and if it goes above a threshold we will switch the input stream to a dummy stream that will be playing a file on loop with a message of ''Signal Lost''.
Line 402: Line 397:
Also, make sure you have the dummy video already created. This can be done with the following gst-launch-1.0 pipeline:
Also, make sure you have the dummy video already created. This can be done with the following gst-launch-1.0 pipeline:


<source lang=bash>
<syntaxhighlight lang="bash">
gst-launch-1.0 videotestsrc pattern=ball num-buffers=300 ! video/x-raw,width=1280,height=720,format=NV12 ! textoverlay text="Signal Lost" valignment=center ! nvvidconv ! nvv4l2h265enc ! mpegtsmux ! filesink location=dummy_video.ts
gst-launch-1.0 videotestsrc pattern=ball num-buffers=300 ! video/x-raw,width=1280,height=720,format=NV12 ! textoverlay text="Signal Lost" valignment=center ! nvvidconv ! nvv4l2h265enc ! mpegtsmux ! filesink location=dummy_video.ts
</source>
</syntaxhighlight>


=====Full Example=====
=====Full Example=====


<source lang=c>
<syntaxhighlight lang="C">
#include <glib.h>
#include <glib.h>
#include <signal.h>
#include <signal.h>
Line 531: Line 526:
   return ret;
   return ret;
}
}
</source>
</syntaxhighlight>


=====Example Walkthrough=====
=====Example Walkthrough=====
Line 537: Line 532:
1. Include headers required for using signals, gstd and GLib functions.
1. Include headers required for using signals, gstd and GLib functions.


<source lang=c>
<syntaxhighlight lang="C">
#include <glib.h>
#include <glib.h>
#include <signal.h>
#include <signal.h>


#include "libgstc.h"
#include "libgstc.h"
</source>
</syntaxhighlight>


2. Define pipelines descriptions.
2. Define pipelines descriptions.


<source lang=c>
<syntaxhighlight lang="C">
#define EXTRACTOR_NAME "pipe"
#define EXTRACTOR_NAME "pipe"
#define EXTRACTOR_PIPE "filesrc location=<path-to-yuy2-1280-722-file-with-metadata> ! \
#define EXTRACTOR_PIPE "filesrc location=<path-to-yuy2-1280-722-file-with-metadata> ! \
Line 562: Line 557:
#define DUMMY_PIPE "multifilesrc location=<path-to-your-dummy-video> loop=true \
#define DUMMY_PIPE "multifilesrc location=<path-to-your-dummy-video> loop=true \
   ! tsdemux ! mpegtsmux ! interpipesink name=dummy_video sync=true"
   ! tsdemux ! mpegtsmux ! interpipesink name=dummy_video sync=true"
</source>
</syntaxhighlight>


Remember to set the ''<path-to-yuy2-1280-722-file-with-metadata>'', ''<path-to-your-dummy-video>'', ''<client-ip>'' and ''<client-port>'' variables according to your environment.
Remember to set the ''<path-to-yuy2-1280-722-file-with-metadata>'', ''<path-to-your-dummy-video>'', ''<client-ip>'' and ''<client-port>'' variables according to your environment.
Line 568: Line 563:
3. Define some important global variables and macros.
3. Define some important global variables and macros.


<source lang=c>
<syntaxhighlight lang="C">
#define DROP_STR_PARSE "\"drop-count\" : "
#define DROP_STR_PARSE "\"drop-count\" : "
#define DROP_STR_LEN 15
#define DROP_STR_LEN 15
#define RECOVER_STR_PARSE "\"stream-recovered\""
#define RECOVER_STR_PARSE "\"stream-recovered\""
</source>
</syntaxhighlight>


These are for parsing the bus messages that we are sending from the extractor element.
These are for parsing the bus messages that we are sending from the extractor element.
Line 578: Line 573:
4. Create a signal mechanism to stop the program properly.
4. Create a signal mechanism to stop the program properly.
   
   
<source lang=c>
<syntaxhighlight lang="C">
static int running = 1;
static int running = 1;


Line 587: Line 582:
   running = 0;
   running = 0;
}
}
</source>
</syntaxhighlight>


This piece of code here will control the while thread in case of some ''Ctrl+C'' interruption to properly free all the resources.
This piece of code here will control the while thread in case of some ''Ctrl+C'' interruption to properly free all the resources.
Line 593: Line 588:
5. Define the main function and some local variables we will be using.
5. Define the main function and some local variables we will be using.


<source lang=c>
<syntaxhighlight lang="C">
gint
gint
main (gint argc, gchar * argv[])
main (gint argc, gchar * argv[])
Line 609: Line 604:
   const gint max_dropped = 5;
   const gint max_dropped = 5;
   const guint64 bus_timeout = 5000000000;
   const guint64 bus_timeout = 5000000000;
</source>
</syntaxhighlight>


6. Create the Gstreamer Daemon client.
6. Create the Gstreamer Daemon client.


<source lang=c>
<syntaxhighlight lang="C">
ret = gstc_client_new (address, port, wait_time, keep_open, &client);
ret = gstc_client_new (address, port, wait_time, keep_open, &client);
   if (GSTC_OK != ret) {
   if (GSTC_OK != ret) {
Line 619: Line 614:
     goto out;
     goto out;
   }
   }
</source>
</syntaxhighlight>


Keep in mind that this function will fail if you haven't started the gstd server.
Keep in mind that this function will fail if you haven't started the gstd server.
Line 625: Line 620:
7. Create the pipelines and set them to play.
7. Create the pipelines and set them to play.


<source lang=c>
<syntaxhighlight lang="C">
   gstc_pipeline_create (client, EXTRACTOR_NAME, EXTRACTOR_PIPE);
   gstc_pipeline_create (client, EXTRACTOR_NAME, EXTRACTOR_PIPE);
   gstc_pipeline_create (client, DUMMY_NAME, DUMMY_PIPE);
   gstc_pipeline_create (client, DUMMY_NAME, DUMMY_PIPE);
Line 634: Line 629:
   g_print ("Press ctrl+c to stop the pipeline...\n");
   g_print ("Press ctrl+c to stop the pipeline...\n");
   signal (SIGINT, sig_handler);
   signal (SIGINT, sig_handler);
</source>
</syntaxhighlight>


8. Start the main loop to listen on bus messages of the extractor pipeline.
8. Start the main loop to listen on bus messages of the extractor pipeline.


<source lang=c>
<syntaxhighlight lang="C">
/* Loop for waiting on the stream-lost and stream-recovered messages. If no
/* Loop for waiting on the stream-lost and stream-recovered messages. If no
     message is received in 5 seconds, exit the loop */
     message is received in 5 seconds, exit the loop */
Line 647: Line 642:
         &message);
         &message);
     if (GSTC_OK == ret) {
     if (GSTC_OK == ret) {
</source>
</syntaxhighlight>


Here as per simplicity and our gst-extractor custom parser code we expect frames to be dropped within less than 5 seconds so it's ok to have a limited timeout for waiting for the message, but you are free to handle this according to your use case.
Here as per simplicity and our gst-extractor custom parser code we expect frames to be dropped within less than 5 seconds so it's ok to have a limited timeout for waiting for the message, but you are free to handle this according to your use case.
Line 653: Line 648:
9. Parse the received element message to see if it has the '''stream-lost''' message structure.
9. Parse the received element message to see if it has the '''stream-lost''' message structure.


<source lang=c>
<syntaxhighlight lang="C">
       str = g_strstr_len (message, -1, DROP_STR_PARSE);
       str = g_strstr_len (message, -1, DROP_STR_PARSE);
       if (str) {
       if (str) {
Line 670: Line 665:
         continue;
         continue;
       }
       }
</source>
</syntaxhighlight>


Here we are looking for the '''drop-count''' message part, and if it exists we query the number of frames that have been dropped consecutively. If this value is greater than the threshold and we are not already listening to the dummy source, then switch to the dummy source to let the streaming client know that something happened.
Here we are looking for the '''drop-count''' message part, and if it exists we query the number of frames that have been dropped consecutively. If this value is greater than the threshold and we are not already listening to the dummy source, then switch to the dummy source to let the streaming client know that something happened.
Line 676: Line 671:
10. Parse the received message to see if it has the '''stream-recovered''' structure instead.
10. Parse the received message to see if it has the '''stream-recovered''' structure instead.


<source lang=c>
<syntaxhighlight lang="C">
/* Parse the message to find out if the stream is recovered */
/* Parse the message to find out if the stream is recovered */
       str = g_strstr_len (message, -1, RECOVER_STR_PARSE);
       str = g_strstr_len (message, -1, RECOVER_STR_PARSE);
Line 688: Line 683:
       /* Reset the pointer */
       /* Reset the pointer */
       str = NULL;
       str = NULL;
</source>
</syntaxhighlight>


If the message was not a stream-lost message then it can be a '''stream-recovered''' message, which leads us to switch back to the main source with the extractor element.
If the message was not a stream-lost message then it can be a '''stream-recovered''' message, which leads us to switch back to the main source with the extractor element.
Line 694: Line 689:
11. Exit the loop if no message was received during the given timeout.
11. Exit the loop if no message was received during the given timeout.


<source lang=c>
<syntaxhighlight lang="C">
     } else {
     } else {
       g_printerr ("Unable to read from bus: %d\n", ret);
       g_printerr ("Unable to read from bus: %d\n", ret);
Line 700: Line 695:
     }
     }
   }
   }
</source>
</syntaxhighlight>


In our case this will only happen when the video file with embedded metadata has reached the end of the stream.
In our case this will only happen when the video file with embedded metadata has reached the end of the stream.
Line 706: Line 701:
12. Get the total dropped frames count and free the resources.
12. Get the total dropped frames count and free the resources.


<source lang=c>
<syntaxhighlight lang="C">
   g_print ("Total dropped %d\n", total_drop_count);
   g_print ("Total dropped %d\n", total_drop_count);
   gstc_pipeline_stop (client, EXTRACTOR_NAME);
   gstc_pipeline_stop (client, EXTRACTOR_NAME);
Line 719: Line 714:
   return ret;
   return ret;
}
}
</source>
</syntaxhighlight>


=====Testing the Example=====
=====Testing the Example=====
Line 727: Line 722:
1. Run Gstreamer Daemon server:
1. Run Gstreamer Daemon server:


<source lang=bash>
<syntaxhighlight lang="bash">
gstd
gstd
</source>
</syntaxhighlight>


2. Run the receiving pipeline on the client side:
2. Run the receiving pipeline on the client side:


<source lang=bash>
<syntaxhighlight lang="bash">
PORT=12345
PORT=12345
GST_DEBUG=ERROR,*seiextract*:MEMDUMP gst-launch-1.0 udpsrc port={PORT} ! tsdemux name=demux demux. ! queue !  h265parse ! seiextract ! avdec_h265 ! autovideosink sync=false demux. ! queue ! 'meta/x-klv' ! metasink sync=false
GST_DEBUG=ERROR,*seiextract*:MEMDUMP gst-launch-1.0 udpsrc port={PORT} ! tsdemux name=demux demux. ! queue !  h265parse ! seiextract ! avdec_h265 ! autovideosink sync=false demux. ! queue ! 'meta/x-klv' ! metasink sync=false
</source>
</syntaxhighlight>


3. In another terminal of the server side run the example:
3. In another terminal of the server side run the example:


<source lang=bash>
<syntaxhighlight lang="bash">
./example
./example
</source>
</syntaxhighlight>


Now you should see on the screen of the receiving side that every time the extractor drops more than 5 frames it will switch to the dummy video stream, and if it recovers it will go back to the extractor pipeline stream.
Now you should see on the screen of the receiving side that every time the extractor drops more than 5 frames it will switch to the dummy video stream, and if it recovers it will go back to the extractor pipeline stream.
Line 752: Line 747:
1. First we start the TCP asynchronous metadata source:
1. First we start the TCP asynchronous metadata source:


<source lang=bash>
<syntaxhighlight lang="bash">
TCP_SERVER_IP="127.0.0.1"
TCP_SERVER_IP="127.0.0.1"
TCP_PORT="3001"
TCP_PORT="3001"
gst-launch-1.0 metasrc is-live=true period=1 metadata=test_async ! meta/x-klv ! tcpserversink host=${TCP_SERVER_IP} port=${TCP_PORT} -v
gst-launch-1.0 metasrc is-live=true period=1 metadata=test_async ! meta/x-klv ! tcpserversink host=${TCP_SERVER_IP} port=${TCP_PORT} -v
</source>
</syntaxhighlight>


2. Then we start the final receiver pipeline which will receive the video with the SEI metadata and both types of KLV metadata through MPEG-TS.
2. Then we start the final receiver pipeline which will receive the video with the SEI metadata and both types of KLV metadata through MPEG-TS.


<source lang=bash>
<syntaxhighlight lang="bash">
FINAL_CLIENT_PORT="12345"
FINAL_CLIENT_PORT="12345"
GST_DEBUG=2,*seiextract*:9 gst-launch-1.0 udpsrc port=${FINAL_CLIENT_PORT} ! tsdemux name=demux demux. ! queue !  h265parse ! seiextract ! avdec_h265 ! autovideosink sync=false demux. ! queue ! meta/x-klv,stream_type=6 ! metasink sync=false async=false demux. ! queue ! meta/x-klv,stream_type=21 ! metasink sync=false async=false
GST_DEBUG=2,*seiextract*:9 gst-launch-1.0 udpsrc port=${FINAL_CLIENT_PORT} ! tsdemux name=demux demux. ! queue !  h265parse ! seiextract ! avdec_h265 ! autovideosink sync=false demux. ! queue ! meta/x-klv,stream_type=6 ! metasink sync=false async=false demux. ! queue ! meta/x-klv,stream_type=21 ! metasink sync=false async=false
</source>
</syntaxhighlight>


Note that here the '''stream_type''' part of the caps is used to identify what kind of KLV metadata we are expecting to receive from each pad. The '''stream_type=6''' is used for asynchronous metadata, while '''stream_type=21''' is for synchronous metadata. On the other side the debug of the seiextract element allows us to see in a quick way the contents of the SEI metadata of the video buffers.
Note that here the '''stream_type''' part of the caps is used to identify what kind of KLV metadata we are expecting to receive from each pad. The '''stream_type=6''' is used for asynchronous metadata, while '''stream_type=21''' is for synchronous metadata. On the other side the debug of the seiextract element allows us to see in a quick way the contents of the SEI metadata of the video buffers.
Line 769: Line 764:
3. Finally to see everything working, run the intermediate pipeline, which is the one having the extractor element.
3. Finally to see everything working, run the intermediate pipeline, which is the one having the extractor element.


<source lang=bash>
<syntaxhighlight lang="bash">
TCP_SERVER_IP="127.0.0.1"
TCP_SERVER_IP="127.0.0.1"
TCP_PORT="3001"
TCP_PORT="3001"
Line 776: Line 771:
INPUT="<path-to-yuy2-1280-722-file-with-metadata>"
INPUT="<path-to-yuy2-1280-722-file-with-metadata>"
gst-launch-1.0 mpegtsmux name=mux alignment=7 ! udpsink host=${FINAL_CLIENT_IP} port=${FINAL_CLIENT_PORT} filesrc location=${INPUT} ! videoparse width=1280 height=722 format=yuy2 framerate=30 ! extractor name=ext data-size=34 height-padding=2 ! queue ! videorate ! nvvidconv ! nvv4l2h265enc name=encoder bitrate=2000000 iframeinterval=300 vbv-size=33333 insert-sps-pps=true control-rate=constant_bitrate profile=Main num-B-Frames=0 ratecontrol-enable=true preset-level=UltraFastPreset EnableTwopassCBR=false maxperf-enable=true ! seiinject ! h265parse ! queue ! mux. ext.klv ! meta/x-klv,stream_type=21 ! queue ! mux. tcpclientsrc host=${TCP_SERVER_IP} port=${TCP_PORT} ! queue ! meta/x-klv,stream_type=6 ! mux.meta_54 -v
gst-launch-1.0 mpegtsmux name=mux alignment=7 ! udpsink host=${FINAL_CLIENT_IP} port=${FINAL_CLIENT_PORT} filesrc location=${INPUT} ! videoparse width=1280 height=722 format=yuy2 framerate=30 ! extractor name=ext data-size=34 height-padding=2 ! queue ! videorate ! nvvidconv ! nvv4l2h265enc name=encoder bitrate=2000000 iframeinterval=300 vbv-size=33333 insert-sps-pps=true control-rate=constant_bitrate profile=Main num-B-Frames=0 ratecontrol-enable=true preset-level=UltraFastPreset EnableTwopassCBR=false maxperf-enable=true ! seiinject ! h265parse ! queue ! mux. ext.klv ! meta/x-klv,stream_type=21 ! queue ! mux. tcpclientsrc host=${TCP_SERVER_IP} port=${TCP_PORT} ! queue ! meta/x-klv,stream_type=6 ! mux.meta_54 -v
</source>
</syntaxhighlight>


Note that this pipeline is in charge of muxing all the streams into the MPEG-TS container and it requires the TCP metadata source to be available when starting.
Note that this pipeline is in charge of muxing all the streams into the MPEG-TS container and it requires the TCP metadata source to be available when starting.
Line 782: Line 777:
Once this pipeline is running you will see on the receiver side the SEI metadata messages as part of the Gstreamer debug and the hexadecimal/ascii dump of the KLV messages on the standard output similar to this:
Once this pipeline is running you will see on the receiver side the SEI metadata messages as part of the Gstreamer debug and the hexadecimal/ascii dump of the KLV messages on the standard output similar to this:


<source lang=bash>
<syntaxhighlight lang="bash">
# Sync KLV
# Sync KLV
00000000 (0x7f335402f4d5): 73 6f 6d 65 20 6b 6c 76 20 30 31 32 30 00        some klv 0120.   
00000000 (0x7f335402f4d5): 73 6f 6d 65 20 6b 6c 76 20 30 31 32 30 00        some klv 0120.   
Line 794: Line 789:
0:01:04.052910196  6320 0x55ef8bcbaf70 MEMDUMP          seiextract gstseiextract.c:357:gst_sei_extract_extract_h265_data:<seiextract0> 00000010: 3a 30 30 20                                      :00             
0:01:04.052910196  6320 0x55ef8bcbaf70 MEMDUMP          seiextract gstseiextract.c:357:gst_sei_extract_extract_h265_data:<seiextract0> 00000010: 3a 30 30 20                                      :00             
0:01:04.052913389  6320 0x55ef8bcbaf70 MEMDUMP          seiextract gstseiextract.c:357:gst_sei_extract_extract_h265_data:<seiextract0> ---------------------------------------------------------------------------
0:01:04.052913389  6320 0x55ef8bcbaf70 MEMDUMP          seiextract gstseiextract.c:357:gst_sei_extract_extract_h265_data:<seiextract0> ---------------------------------------------------------------------------
</source>
</syntaxhighlight>


==Contact Us==
==Contact Us==
Cookies help us deliver our services. By using our services, you agree to our use of cookies.