⬆️ ⬇️

Getting to know GStreamer: items and containers

image



Hello again, a habrayur interested in the GStreamer framework. In the last article, we talked about how to initialize the library for full-fledged work with it. And today we will analyze the process of creating elements and the layout of the pipeline. As a practical material, a simple copier of files (like cp ) will be created - yes, yes, GStreamer is so harsh that they can almost open a beer. So, go ahead!



Creating an item with a factory



The definition of the element is available and is set out here with pictures, but some clarification is needed. The set of plug-ins installed in the system (by the way, they are divided into Core, Base, Good, Ugly, Bad, etc., depending on the quality of the plugin and the absence / presence of licensing problems) determine the list of factories for creating elements. Let's see which factories are available for elements of type Source.



#include <gst/gst.h> int main (int argc, char * argv[]) { /*  ,       */ GList *list; /*  GStreamer */ gst_init (NULL, NULL); list = gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_SRC, GST_RANK_NONE); GList *l; for (l = list; l != NULL; l = l->next) g_print ("%s\n", gst_object_get_name (l->data)); gst_plugin_feature_list_free (list); gst_plugin_feature_list_free (l); return 0; } 


Result:

')

 pulsesrc alsasrc dataurisrc filesrc jackaudiosrc rtmpsrc ... 


As you can see, there are quite a few sources (you can view the list of all factories using the macro GST_ELEMENT_FACTORY_TYPE_ANY). Pay attention to filesrc - we use it in the practical part.



So, to create an element, we are looking for a factory with the desired name (for example, filesrc):



 GstElementFactory *factory; factory = gst_element_factory_find ("filesrc"); 


And then directly create the element by giving it a name. By this name you can then access the element, and this is also convenient when debugging.



 GstElement *element; element = gst_element_factory_create (factory, "elname"); 


There is a shortcut for these two functions:



 GstElement *gst_element_factory_make (const gchar *factoryname, const gchar *name); 


Each created element has a set of properties that can be controlled and, thus, customized. Setting and reading properties is performed using set and get methods:



 void g_object_set (gpointer object, const gchar *first_property_name, ...); void g_object_get (gpointer object, const gchar *first_property_name, ...); 


For example, set the element property "name":



 g_object_set (G_OBJECT(element), "name", "another_name", NULL); 


Five cents of the four states



All created elements can be in one of four states:





You can switch item states using the function:



 GstStateChangeReturn gst_element_set_state (GstElement *element, GstState state); 


It is worth noting that the switch can be through. Those. if the element in the NULL state is switched to PLAYING, it will automatically go through all intermediate states.



Special items - containers and conveyors



Now imagine that you have a set of ten elements, and you want to switch each element, for example, to the PLAYING state. It would be ridiculous if you had to call the function gst_element_set_state () 10 times for this. There is a special element in which other elements can be placed - the container (bin). Having placed several elements in a container, you can manage them as a single entity, for example, to switch the state.



No need to think that the container is something separate. No, this is the same element of the GStreamer ecosystem as any other element. So you can create it using the factory:



 GstElement *bin; bin = gst_element_factory_make ("bin", "bin_name"); 


Also for this operation there is an auxiliary function:



 GstElement *gst_bin_new (const gchar *name); 


To manage the synchronization and processing messages from the tires (we’ll talk about tires and messages next time), a top-level container is allocated - the pipeline. In any application using containers, there must be at least one pipeline.



The pipeline is created either with the help of a factory (“pipeline” factory), or with an auxiliary function:



 GstElement *gst_pipeline_new (const gchar *name); 


Adding items to the container and linking



You can add items to a container (or conveyor) or remove them from there using functions:



 gboolean gst_bin_add (GstBin *bin, GstElement *element); void gst_bin_add_many (GstBin *bin, GstElement *element_1, ..., NULL); gboolean gst_bin_remove (GstBin *bin, GstElement *element); 


Each created item has a so-called. pads (pad) - points through which you can associate an element with other elements and, thus, create a working media conveyor. This concept is the core of GStreamer.



Linking is performed by functions:



 gboolean gst_element_link (GstElement *src, GstElement *dest); gboolean gst_element_link_many (GstElement *element_1, GstElement *element_2, ..., NULL); 


Not all pads are compatible with each other. Therefore, before the elements are linked, the compatibility check process takes place automatically.



We must not forget that before binding the elements they must be added to the pipeline. Also, when adding elements to the pipeline, in which the related elements are already located, their links disappear.



Practice



To consolidate the theoretical material we will write an application that performs copying from a file to a file. For this we will use two elements from the Core set - filesrc and filesink. Our pipeline will look like this:



image



So let's go!



 #include <gst/gst.h> int main (int argc, char * argv[]) { if (argc != 3) { g_print ("Syntax error\n"); return -1; } GstElement *pipeline, *src, *dst; /*       . */ GstStateChangeReturn ret; /* bus -   .        . */ GstBus *bus; GstMessage *msg; /*  GStreamer */ gst_init (NULL, NULL); /*   */ pipeline = gst_element_factory_make ("pipeline", "pipe"); src = gst_element_factory_make ("filesrc", "src"); dst = gst_element_factory_make ("filesink", "dst"); if ( !pipeline || !src || !dst ) { g_printerr ("Unable to create some elements\n"); return -1; } /*     */ gst_bin_add_many (GST_BIN(pipeline), src, dst, NULL); /*    */ if ( gst_element_link (src, dst) != TRUE ) { g_printerr ("Elements can not be linked\n"); gst_object_unref (pipeline); return -1; } /*    */ g_object_set (src, "location", argv[1], NULL); g_object_set (dst, "location", argv[2], NULL); /*   */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if ( ret == GST_STATE_CHANGE_FAILURE ) { g_printerr ("Unable to set pipeline to the playing state\n"); gst_object_unref (pipeline); return -1; } /*     PLAYING.     ,  * .       (   *    ) */ bus = gst_element_get_bus (pipeline); /*     .   ,   * ,    . */ msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); /*   */ if (msg != NULL) { GError *err; gchar *debug_info; switch ( GST_MESSAGE_TYPE (msg) ) { case GST_MESSAGE_ERROR: gst_message_parse_error (msg, &err, &debug_info); g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message); g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none"); g_clear_error (&err); g_free (debug_info); break; case GST_MESSAGE_EOS: g_print ("We reach End-Of-Stream\n"); break; default: g_printerr ("Unexpected message received\n"); break; } gst_message_unref (msg); } /*   */ gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return 0; } 


Compile and run:



 $ gcc -Wall -o cp cp.c $(pkg-config --cflags --libs gstreamer-1.0) $ echo 'hello world' > file.txt $ ./cp file.txt another_file.txt We reach End-Of-Stream $ cat another_file.txt hello world 


Conclusion



The next article will look at the bus and the various types of messages that are transmitted through it. And to consolidate, we will write an application for lovers of singing karaoke!



Materials on the topic



GStreamer Application Development Manual

GStreamer 1.0 Core Reference Manual

GStreamer Core Plugins Reference

Source: https://habr.com/ru/post/251427/



All Articles