Streaming Your Screen Using GStreamer C++
Tags: cpp, tutorial, beginners, programming
Published: 2023-11-18 01:46:24

## 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:

```cpp
#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: 

```cpp
int main(int argc, char *argv[])
{

}
```

Next we will be create the GStreamer objects needed for this example, which are the following:

```cpp
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:

```cpp
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.

```cpp
g_object_set(sink, "location", "rtmp://localhost/stream", NULL);
```

Now we need to create the GStreamer pipeline:

```cpp
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:

```cpp
#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:

```cpp
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:

```cpp
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:

```cpp
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.

```cpp
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:

```cpp
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:

```cpp
#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:

```txt
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.

```bash
mkdir build && cd build
```

Now we compile the code via the following commands:

```bash
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:

```bash
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:

```bash
./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. 

[![“Buy Me A Coffee”](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/ethand9999)

If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the [following course](https://algolab.so/p/algorithms-and-data-structure-video-course?affcode=1413380_bzrepgch)

If you are interested in using a VPN, I recommend ExpressVPN. If you use the below link you get 30 days for [free](https://www.expressrefer.com/refer-a-friend/30-days-free?referrer_id=93229826&utm_campaign=referrals&utm_medium=copy_link&utm_source=referral_dashboard)