Introduction
Hello! π In this tutorial we will be using C++ for screen capturing and streaming via RTMP! This tutorial will provide a pratical example capturing your computer screen and streaming it to an RTMP server using GStreamer and C++.
Setting up the Environment
If you've followed any of my previous tutorials you should already have GStreamer installed on your system. Installation varies depending on the Operating System you are using.
Writing the Code
Before we can stream our screen to the RTMP server we first actually need to write the code. Open a file called "main.cpp" in your favourite text editor and include the following libraries at the top of the file:
#include <gst/gst.h>
#include <iostream>
Next we need to create the entry point of the C++ program called the main function, add the following below the above:
int main(int argc, char *argv[])
{
}
Next we will be create the GStreamer objects needed for this example, which are the following:
GstElement *pipeline, *source, *encoder, *converter, *sink, *queue1, *queue2, *queue3, *flvmux;
GstBus *bus;
GstMessage *message;
GstStateChangeReturn ret;
gst_init(&argc, &argv);
converter = gst_element_factory_make("videoconvert", "converter");
encoder = gst_element_factory_make("x264enc", "encoder");
sink = gst_element_factory_make("rtmpsink", "sink");
queue1 = gst_element_factory_make("queue", "queue1");
queue2 = gst_element_factory_make("queue", "queue2");
queue3 = gst_element_factory_make("queue", "queue3");
flvmux = gst_element_factory_make("flvmux", "flvmux");
In the above code we initialize GStreamer via the gst_init. We also create all the elements needed which are the following:
converter: This converts frames to a format suitable for encoding.
encoder: Encodes the video into H264 format
sink: Sends the encoded video to an RTMP server
queue1, queue2, queue3: These are queue elements used to buffer data between pipeline stages
flvmux: Packages the encoded video into FLV format
Next we need to check if the elements were created successfully, if not we will exit the program this is done via the following code:
if (!converter || !encoder || !sink || !queue1 || !queue2 || !queue3 || !flvmux)
{
std::cerr << "Not all elements could be created." << std::endl;
return -1;
}
Once the elements are created we next need to set the location for the rtmpsink object, feel free to change the rtmp url to the url of your rtmp server.
g_object_set(sink, "location", "rtmp://localhost/stream", NULL);
Now we need to create the GStreamer pipeline:
pipeline = gst_pipeline_new("pipeline");
The source element we will use to capture the screen will vary depending on what your operating system is. The following will create a source element depending on what operating system you are using:
#if defined(_WIN32) || defined(_WIN64) source = gst_element_factory_make("dx9screencapsrc", "source"); #elif defined(__APPLE__) source = gst_element_factory_make("avfvideosrc", "source"); #else source = gst_element_factory_make("ximagesrc", "source"); #endif
Now that we have created the elements and the pipeline we need to add them all together:
gst_bin_add_many(GST_BIN(pipeline), source, converter, queue1, encoder, queue2, flvmux, queue3, sink, NULL);
Next we need to link the elements together this can be done via the following:
if (!gst_element_link_many(source, converter, queue1, encoder, queue2, flvmux, queue3, sink, NULL)) {
std::cerr << "Elements could not be linked" << std::endl;
gst_object_unref(pipeline);
return -1;
}
Once the elements are successfully linked we can now try and set the pipeline state to playing:
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
This will start the capturing and streaming process.
Next we will retrieve the bus from the pipeline and wait for either an ERROR or EOS (end-of-stream) message.
bus = gst_element_get_bus(pipeline);
message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GstMessageType(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
Finally after streaming we need to cleanup and release the resources, this can be done via:
if (message != NULL)
gst_message_unref(message);
gst_object_unref(bus);
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
The final code should look like the following:
#include <gst/gst.h>
#include <iostream>
int main(int argc, char *argv[])
{
GstElement *pipeline, *source, *encoder, *converter, *sink, *queue1, *queue2, *queue3, *flvmux;
GstBus *bus;
GstMessage *message;
GstStateChangeReturn ret;
gst_init(&argc, &argv);
converter = gst_element_factory_make("videoconvert", "converter");
encoder = gst_element_factory_make("x264enc", "encoder");
sink = gst_element_factory_make("rtmpsink", "sink");
queue1 = gst_element_factory_make("queue", "queue1");
queue2 = gst_element_factory_make("queue", "queue2");
queue3 = gst_element_factory_make("queue", "queue3");
flvmux = gst_element_factory_make("flvmux", "flvmux");
if (!converter || !encoder || !sink || !queue1 || !queue2 || !queue3 || !flvmux)
{
std::cerr << "Not all elements could be created." << std::endl;
return -1;
}
g_object_set(sink, "location", "rtmp://localhost/stream", NULL);
pipeline = gst_pipeline_new("pipeline");
if (!pipeline)
{
std::cerr << "Pipeline could not be created." << std::endl;
return -1;
}
#if defined(_WIN32) || defined(_WIN64)
source = gst_element_factory_make("dx9screencapsrc", "source");
#elif defined(__APPLE__)
source = gst_element_factory_make("avfvideosrc", "source");
#else
source = gst_element_factory_make("ximagesrc", "source");
#endif
if (!source)
{
std::cerr << "Screen capture source could not be created." << std::endl;
gst_object_unref(pipeline);
return -1;
}
gst_bin_add_many(GST_BIN(pipeline), source, converter, queue1, encoder, queue2, flvmux, queue3, sink, NULL);
if (!gst_element_link_many(source, converter, queue1, encoder, queue2, flvmux, queue3, sink, NULL))
{
std::cerr << "Elements could not be linked" << std::endl;
gst_object_unref(pipeline);
return -1;
}
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE)
{
std::cerr << "Unable to set the pipeline to playing state" << std::endl;
gst_object_unref(pipeline);
return -1;
}
bus = gst_element_get_bus(pipeline);
message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GstMessageType(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
if (message != NULL)
{
gst_message_unref(message);
}
gst_object_unref(bus);
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
return 0;
}
Compiling the Source Code
Next in order to run the source code we need to compile it, we will use CMake for this. Create a new file called "CMakeLists.txt" and populate it with the following:
cmake_minimum_required(VERSION 3.10)
project(ScreenCapture)
# Find the GStreamer libraries
find_package(PkgConfig)
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules(GSTREAMER_VIDEO REQUIRED gstreamer-video-1.0)
# Include GStreamer headers
include_directories(${GSTREAMER_INCLUDE_DIRS})
link_directories(${GSTREAMER_LIBRARY_DIRS})
# Add the executable
add_executable(screen_capture main.cpp)
# Link the GStreamer libraries
target_link_libraries(screen_capture ${GSTREAMER_LIBRARIES} ${GSTREAMER_VIDEO_LIBRARIES})
# Set C++ standard
set_property(TARGET screen_capture PROPERTY CXX_STANDARD 11)
Next we will create a build directory to compile the code.
mkdir build && cd build
Now we compile the code via the following commands:
cmake ..
make
You should now see a executable called "screen_capture"
Running the Code
Now we can finally run the code, if you don't have an RTMP server you can use the following command to set one up:
docker run --rm -it -e MTX_PROTOCOLS=tcp -p 8554:8554 -p 1935:1935 -p 8888:8888 -p 8889:8889 aler9/rtsp-simple-server
Once the server is started you can now run the example via the following command:
./screen_capture
Now you can use a media player like VLC to view the screen, just point the source to the following URL "rtmp://localhost:1935/stream" and you should see your screen being streamed. π
Conclusion
Thats it! You've just created a screen capturing and streaming application using GStreamer and C++! This tutorial covered the basics of setting up a GStreamer pipeline in C++, feel free to try and create more complex multimedia applications! I hope this tutorial helped you the same I had fun creating it. π
As always you can find the sample code used for this tutorial on my GitHub: https://github.com/ethand91/screen-streaming
Happy Coding! π
Like my work? I post about a variety of topics, if you would like to see more please like and follow me. Also I love coffee.
If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the following course
If you are interested in using a VPN, I recommend ExpressVPN. If you use the below link you get 30 days for free