Using Gstreamer in your Flutter project Pt.1: C file

In this part of the series, we'll create a C program to generate a simple sine wave using GStreamer. In the following videos, we'll explore how to use Flutter to call functions from this C program.

Prerequisites

Before following this series, ensure you have:

  • Basic knowledge of the C programming language.

  • Basic knowledge of Dart/Flutter.

  • Basic knowledge of CMake.

  • Basic knowledge of GStreamer.

Required Tools and Software

This part of the series is demonstrated on a Linux PC. You may adapt the instructions for other operating systems. Below are the common requirements:

  • GStreamer Libraries: Install the GStreamer and its development libraries. Follow the GStreamer documentation for installation guidelines.

  • Code Editor: Any code editor will work, but I use VSCode.

  • CMake: Download from cmake.org. On Linux, you can install it via your package manager.

GStreamer Pipeline

The C program we'll build includes a GStreamer pipeline that looks like this:

gst-launch-1.0 audiotestsrc ! audioconvert ! autoaudiosink

Creating the C Program

First, create a directory for your project:

mkdir fltgst

Next, open VSCode (or your preferred code editor) and create the main.c file. The C code for our simple pipeline is as follows:

#include <gst/gst.h>

// gst-launch-1.0 audiotestsrc ! audioconvert ! autoaudiosink

typedef struct _FltGstData
{
    GstElement *pipeline;
    GstElement *audiotestsrc;
    GstElement *audioconvert;
    GstElement *autoaudiosink;

    GMainLoop *mainloop;
} FltGstData;

FltGstData *data;

void init(void)
{
    // init
    gst_init(NULL, NULL);
    data = (FltGstData *)malloc(sizeof(FltGstData));
}

void setup_pipeline(void)
{
    // setup pipeline
    data->audiotestsrc = gst_element_factory_make("audiotestsrc", NULL);
    data->audioconvert = gst_element_factory_make("audioconvert", NULL);
    data->autoaudiosink = gst_element_factory_make("autoaudiosink", NULL);
    data->pipeline = gst_pipeline_new(NULL);

    data->mainloop = g_main_loop_new(NULL, FALSE);

    if (!data->audiotestsrc || !data->audioconvert || !data->autoaudiosink || !data->pipeline)
    {
        g_printerr("Elements could not be created.\n");
        gst_object_unref(data->pipeline);
        return;
    }

    gst_bin_add_many(GST_BIN(data->pipeline), data->audiotestsrc, data->audioconvert, data->autoaudiosink, NULL);
    if (!gst_element_link_many(data->audiotestsrc, data->audioconvert, data->autoaudiosink, NULL))
    {
        g_printerr("Elements could not be created.\n");
        gst_object_unref(data->pipeline);
        return;
    }
}

void start_pipeline(void)
{
    // start pipeline
    GstStateChangeReturn ret;
    ret = gst_element_set_state(data->pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE)
    {
        g_printerr("Elements could not be created.\n");
        gst_object_unref(data->pipeline);
        return;
    }
}

void run_mainloop(void)
{
    // run main loop
    g_main_loop_run(data->mainloop);
}

void free_resource(void)
{
    // free resources
    gst_element_set_state(data->pipeline, GST_STATE_NULL);
    gst_object_unref(data->pipeline);
}

int main(int argc, char const *argv[])
{
    init();
    setup_pipeline();
    start_pipeline();
    run_mainloop();
    free_resource();
    return 0;
}

Building with CMake

We need a build system to handle the compilation and linking of our program. We'll use CMake, which is compatible with Flutter's build system for C code. Write the following CMakeLists.txt file in the same directory as main.c:

cmake_minimum_required(VERSION 3.5)

project(fltgst VERSION 0.0.1
LANGUAGES C)

find_package(PkgConfig REQUIRED)

pkg_search_module(GST REQUIRED gstreamer-1.0)
include_directories(${GST_INCLUDE_DIRS})

add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} PRIVATE ${GST_LDFLAGS})

Compiling and Running

With both files created, you can now build the C program:

cmake . # this will generate the makefiles
make # this is the actual command that build the executable

You should now see an executable named fltgst in your directory:

$ ls -l
total 60
-rw-rw-r-- 1 max max 15893  624 09:56 CMakeCache.txt
drwxrwxr-x 6 max max  4096  624 09:56 CMakeFiles
-rw-rw-r-- 1 max max  1646  624 09:56 cmake_install.cmake
-rw-rw-r-- 1 max max   302  27 20:24 CMakeLists.txt
-rwxrwxr-x 1 max max 16664  624 09:56 fltgst # <- this one
-rw-rw-r-- 1 max max  2044  27 20:24 main.c
-rw-rw-r-- 1 max max  5125  624 09:56 Makefile

Run the executable to hear the sine wave:

./fltgst

Conclusion

That's it for this video. In the next video, we'll integrate GStreamer with a Flutter project targeting Linux, using similar code to what's in our main.c file.

GitHub Repository

You can find the complete code for this project on GitHub.
Files after changes from this tutorial: https://github.com/fengjiongmax/fltgst/tree/3c73f61a78f4fa34db4e28393c80b95a3169a978