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:
- GST_STATE_NULL
The element enters this state immediately after its creation.
- GST_STATE_READY
In this state, the necessary resources are allocated for the element, so it is prepared to go to the GST_STATE_PAUSED state.
- GST_STATE_PAUSED
In this state, the item is fully open to the data stream, but data is not yet transmitted.
- GST_STATE_PLAYING
This state is completely identical to the previous one, but the data is transferred.
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:
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