DEV Community

Cover image for Streaming Your Screen Using GStreamer C++
Ethan
Ethan

Posted on • Originally published at ethan-dev.com

Streaming Your Screen Using GStreamer C++

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>


Enter fullscreen mode Exit fullscreen mode

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[])
{

}


Enter fullscreen mode Exit fullscreen mode

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");


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

Now we need to create the GStreamer pipeline:



pipeline = gst_pipeline_new("pipeline");


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

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));


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

Next we will create a build directory to compile the code.



mkdir build && cd build


Enter fullscreen mode Exit fullscreen mode

Now we compile the code via the following commands:



cmake ..
make


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Once the server is started you can now run the example via the following command:



./screen_capture


Enter fullscreen mode Exit fullscreen mode

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.

โ€œBuy Me A 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

Top comments (0)