vcodecgst_web_logo

VCodecGst C++ library

v1.0.3

Table of contents

Overview

The VCodecGst C++ library provides video encoding and decoding functions for H264, HEVC (H265), and JPEG codecs, based on GStreamer. The library supports software encoders/decoders and hardware encoders/decoders for Intel, Nvidia Jetson, NXP, V4L2 (for example, Raspberry PI) and Rockchip platforms. The VCodecGst video codec class inherits its interface and data structures from the VCodec interface library. The library depends on the VCodec library (provides codec interface, uses the Frame library), the open-source GStreamer, and the Logger library (provides functions to print logs). The repository does not contain the source code of the Frame, VCodec and Logger libraries (see Build and connect to your project). The library uses the C++17 standard. The library is developed and tested on Linux. The code and the CMake files of the library have no Linux-only parts: with the GStreamer packages for Windows (MSVC) only the software codecs (TYPE 0) could be used on Windows, because the hardware elements of Table 1 exist on Linux only. The Windows build is not tested. Supported platforms and codecs:

VCodecGst is general-purpose software: it is not tied to any sector or application, and the developer of a program decides what the program does with it.

Table 1 - Supported platforms and codecs.

Platform H264 encoder H264 decoder H265 encoder H265 decoder JPEG encoder JPEG decoder
CPU software x264enc avdec_h264 x265enc avdec_h265 jpegenc jpegdec
Intel vaapih264enc vaapih264dec vaapih265enc vaapih265dec jpegenc (SW) jpegdec (SW)
Nvidia Jetson nvv4l2h264enc nvv4l2decoder nvv4l2h265enc nvv4l2decoder nvjpegenc nvjpegdec
NXP vpuenc_h264 vpudec vpuenc_hevc vpudec jpegenc (SW) jpegdec (SW)
V4L2 (Raspberry pi etc) v4l2h264enc v4l2h264dec v4l2h265enc v4l2h265dec jpegenc (SW) jpegdec (SW)
Rockchip (MPP) mpph264enc mppvideodec mpph265enc mppvideodec mppjpegenc mppjpegdec

Versions

Table 2 - Library versions.

Version Release date What’s new
1.0.0 12.11.2025 First version.
1.0.1 13.05.2026 Update documentation.
1.0.2 17.10.2026 - Sub-repositories removed: the dependencies are added next to the library (stand alone build) or by the top-level project.
- Fixed: endless wait of transcode() after an error of the pipeline or a missing GStreamer element, leak of the pipeline after a parameter change or RESET, leak of decoded frame memory, Rockchip encoder pipeline (not created; bitrate in bits per second), decoding of streams with a frame rate other than FPS, duplicated input frames of the decoder, uninitialized memory in the first frame of the HEVC software encoder (x265enc element of GStreamer).
- Input checks: frame size and data, parameter values (NaN, infinity, ranges), JPEG frame size limit, data after the end of sequence NAL unit of H264 access units (it crashes the h264parse element of GStreamer).
- Encoding of frames with a width that is not a multiple of 4.
- Compiler warnings and hardening flags, new test application, documentation update.
1.0.3 25.10.2026 - Example: the video frames are generated (a ball moving over a background with noise) instead of being read from the file test.mp4, OpenCV is not used any more.

Library files

The library is supplied as source code only. The user is provided with a set of files in the form of a CMake project (repository). The repository structure is shown below:

CMakeLists.txt ------------- Main CMake file of the library.
src ------------------------ Folder with library source code.
    CMakeLists.txt --------- CMake file of the library.
    VCodecGst.h ------------ Main library header file.
    VCodecGstVersion.h ----- Header file with library version.
    VCodecGstVersion.h.in -- File for CMake to generate version header.
    VCodecGst.cpp ---------- C++ implementation file.
    impl ------------------- Folder with implementation files.
        VCodecGstImpl.h ---- Header file of the implementation class.
        VCodecGstImpl.cpp -- C++ implementation file of the implementation class.
test ----------------------- Folder for test application.
    CMakeLists.txt --------- CMake file for test application.
    main.cpp --------------- Source C++ file of test application.
example -------------------- Folder for simple example.
    CMakeLists.txt --------- CMake file of example.
    main.cpp --------------- Source C++ file of example.

VCodecGst class description

VCodecGst class declaration

The VCodecGst class is declared in the VCodecGst.h file. Class declaration:

namespace rapidpixel
{
class VCodecGst : public VCodec
{
public:

    /// Class constructor.
    VCodecGst();

    /// Class destructor.
    ~VCodecGst() override;

    /// Copy and move are deleted (the object owns a GStreamer pipeline).
    VCodecGst(const VCodecGst&) = delete;
    VCodecGst(VCodecGst&&) = delete;
    VCodecGst& operator=(const VCodecGst&) = delete;
    VCodecGst& operator=(VCodecGst&&) = delete;

    /// Get library version.
    static std::string getVersion();

    /// Encode or decode video frame.
    bool transcode(Frame& src, Frame& dst) override;

    /// Set parameter value.
    bool setParam(VCodecParam id, float value) override;

    /// Get parameter value.
    float getParam(VCodecParam id) override;

    /// Execute command.
    bool executeCommand(VCodecCommand id) override;
};
}

getVersion method

The getVersion() method returns a string of the current VCodecGst class version. Method declaration:

static std::string getVersion();

The method can be used without a VCodecGst class instance:

std::cout << "VCodecGst class version: " << rapidpixel::VCodecGst::getVersion() << std::endl;

Console output:

VCodecGst class version: 1.0.3

transcode method

The transcode(…) method is intended to encode and decode video frames (Frame class). The video codec encodes/decodes video frames frame-by-frame: one source frame per call. The GStreamer pipeline is created at the first call and created again after a parameter change, the RESET command, a change of the frame size, of the pixel format or of the direction (encoding / decoding) and after an error of the pipeline. The method can be called from several threads: the calls are serialized (a call waits until the previous call is finished). Method declaration:

bool transcode(Frame& src, Frame& dst);
Parameter Value
src Source video frame (see Frame class description). To encode video data, the src frame must have the NV12 pixel format, an even width and height in the range 2 - 8192 and data of at least width x (height + height / 2) bytes. To decode video data, the src frame must have a compressed pixel format (field fourcc of the Frame class): JPEG, H264, or HEVC, and data (size > 0) with one H264 / HEVC access unit (Annex B byte stream) or one JPEG image (starting with the SOI marker). In an H264 access unit only zero bytes can follow an end of sequence or end of stream NAL unit (other data is rejected). The size of an H264 or HEVC frame (fields width and height) must be the size of the frames of the stream; the size of a JPEG frame is read from the image. The size of the decoded frame must be even, in the range 2 - 8192.
dst Result video frame (see Frame class description). To encode video data, the dst frame must have a compressed pixel format (field fourcc of the Frame class): JPEG, H264, or HEVC. The user must specify the output pixel format in advance. If the dst frame has the width and height of the source frame and data (size > 0), the encoded data is written into the memory of the dst frame: the memory must have at least width x height x 4 bytes (the Frame class allocates this size for a compressed pixel format). Otherwise the method allocates the dst frame. The fields frameId and sourceId of the source frame are copied to the dst frame. When decoding, the video codec sets the NV12 pixel format and the size of the decoded frame automatically (the dst frame is allocated if it is not an NV12 frame of this size).

Returns: TRUE if the frame was successfully encoded/decoded or FALSE if not. The method waits up to 1 second for the GStreamer pipeline to take the source frame and up to 1 second for the result. If no result is ready in time, the method returns FALSE and sets the size field of the dst frame to 0: a decoder with a frame delay (for example, for streams with frame reordering) returns FALSE for the first frames and then the frames of the previous calls. When encoding, the dst frame can be allocated and gets the frame IDs before the encoding, also if FALSE is returned; when decoding, other errors do not change the dst frame. The library can not check that the data pointers of the frames are valid for the sizes given with them.

setParam method

The setParam(…) method is designed to set new video codec parameter values. The value is converted to an integer (the fractional part is truncated); NaN, infinity and values outside of the valid range of the parameter (Table 3) are rejected. A new value is applied at the next transcode(…) call (the GStreamer pipeline is created again). The method can be called from any thread. Method declaration:

bool setParam(VCodecParam id, float value);
Parameter Description
id Video codec parameter ID according to the VCodecParam enum.
value Video codec parameter value. Valid values depend on the parameter ID.

Returns: TRUE if the parameter was set successfully or FALSE if not (the parameter keeps its value).

The VCodec.h file of the VCodec library defines IDs for parameters (VCodecParam enum) and IDs for commands (VCodecCommand enum). VCodecParam declaration:

namespace rapidpixel
{
enum class VCodecParam
{
    /// [read/write] Log level: 0 - Disable, 1 - Console, 2 - File,
    /// 3 - Console and file.
    LOG_LEVEL = 1,
    /// [read/write] Bitrate, kbps. For H264 and H265 codecs.
    BITRATE_KBPS,
    /// [read/write] Minimum bitrate, kbps. For variable bitrate mode.
    MIN_BITRATE_KBPS,
    /// [read/write] Maximum bitrate, kbps. For variable bitrate mode.
    MAX_BITRATE_KBPS,
    /// [read/write] Bitrate mode: 0 - constant bitrate, 1 - variable bitrate.
    BITRATE_MODE,
    /// [read/write] Quality 0-100%. For JPEG codecs.
    QUALITY,
    /// [read/write] FPS. For H264 and H265 codecs.
    FPS,
    /// [read/write] GOP size. For H264 and H265 codecs.
    GOP,
    /// [read/write] H264 profile: 0 - Baseline, 1 - Main, 2 - High.
    H264_PROFILE,
    /// [read/write] Codec type. Depends on implementation.
    TYPE,
    /// [read/write] Custom parameter 1. Depends on implementation.
    CUSTOM_1,
    /// [read/write] Custom parameter 2. Depends on implementation.
    CUSTOM_2,
    /// [read/write] Custom parameter 3. Depends on implementation.
    CUSTOM_3
};
}

Table 3 - Video codec parameters description.

Parameter Access Description
LOG_LEVEL read / write Logging mode. Default: 1. Values:
0 - Disable.
1 - Only console (terminal).
2 - Only file.
3 - File and terminal (console).
The file is written by the Logger library if the folder and the name of the log file are set with its static method Logger::setSaveLogParams(…); otherwise nothing is written to a file.
BITRATE_KBPS read / write Bitrate in kbps for H264 and H265 (HEVC) encoding. Valid values: 1 - 2048000. Default: 5000 kbps. The value is given to the encoder element: a value outside of the range of the element is not accepted by the element, it keeps its default bitrate (for example, the software HEVC encoder x265enc accepts up to 102400 kbps). The V4L2 encoders do not get the bitrate.
MIN_BITRATE_KBPS read / write Minimum bitrate in kbps. Only for H264 and H265 (HEVC) codecs. Valid values: >= 0. Default: 2000 kbps. For variable bitrate mode in H264 and H265 (HEVC) encoding. Not used by the GStreamer pipelines.
MAX_BITRATE_KBPS read / write Maximum bitrate in kbps. Only for H264 and H265 (HEVC) codecs. Valid values: >= 0. Default: 8000 kbps. For variable bitrate mode in H264 and H265 (HEVC) encoding. Not used by the GStreamer pipelines.
BITRATE_MODE read / write Bitrate mode for H264 and H265 (HEVC) codecs only. Values: 0 - constant bitrate (default), 1 - variable bitrate. Not used by the GStreamer pipelines.
QUALITY read / write Quality 1 (low quality) - 100 (maximum quality). Default: 20. For JPEG encoding (both hardware and software).
FPS read / write Frame rate (frames per second), >= 1. Default: 30. For encoding it is the frame rate of the encoder input. For decoding it sets the timestamps of the input data; the decoded frames keep the frame rate of the stream.
GOP read / write GOP size (period of key frames) for H264 and H265 (HEVC) encoding, >= 1. Default: 30. Value 1 means each output frame is a key frame, 20 means every 20th frame is a key frame, etc. The V4L2 encoders do not get the GOP size.
H264_PROFILE read / write H264 profile for H264 encoding. Default: 0 (Baseline).
0 - Baseline.
1 - Main.
2 - High.
Note: The profile is used by the software, Nvidia Jetson and NXP encoders. The Intel (VAAPI), V4L2 and Rockchip encoders use their default profile.
TYPE read / write Type of encoder/decoder. Default: 0 (Software).
0 - Software (x264enc/x265enc, avdec_h264/avdec_h265, jpegenc/jpegdec).
1 - Intel hardware (vaapih264enc/vaapih265enc, vaapih264dec/vaapih265dec).
2 - Nvidia Jetson hardware (nvv4l2h264enc/nvv4l2h265enc, nvv4l2decoder, nvjpegenc/nvjpegdec).
3 - NXP hardware (vpuenc_h264/vpuenc_hevc, vpudec).
4 - V4L2 hardware for Raspberry Pi etc. (v4l2h264enc/v4l2h265enc, v4l2h264dec/v4l2h265dec).
5 - Rockchip (MPP) hardware (mpph264enc/mppvideodec, mpph265enc/mppvideodec, mppjpegenc/mppjpegdec).
If the GStreamer elements of the type are not installed, the transcode(…) method returns FALSE.
CUSTOM_1 read / write Not supported by the VCodecGst library.
CUSTOM_2 read / write Not supported by the VCodecGst library.
CUSTOM_3 read / write Not supported by the VCodecGst library.

getParam method

The getParam(…) method is designed to obtain video codec parameter values. The method can be called from any thread. Method declaration:

float getParam(VCodecParam id);
Parameter Description
id Video codec parameter ID according to the VCodecParam enum (see Table 3).

Returns: Parameter value or -1 if the parameter is not supported (CUSTOM_1, CUSTOM_2, CUSTOM_3 and IDs outside of the VCodecParam enum).

executeCommand method

The executeCommand(…) method is designed to execute video codec commands. The method can be called from any thread. Method declaration:

bool executeCommand(VCodecCommand id);
Parameter Description
id Video codec command ID according to the VCodecCommand enum.

Returns: TRUE if the command was successfully executed or FALSE if not (MAKE_KEY_FRAME and IDs outside of the VCodecCommand enum).

The VCodec.h file of the VCodec library defines IDs for parameters (VCodecParam enum) and IDs for commands (VCodecCommand enum). VCodecCommand declaration:

enum class VCodecCommand
{
    /// Reset.
    RESET = 1,
    /// Generate key frame. For H264 and H265 codecs.
    MAKE_KEY_FRAME
};

Table 4 - Video codec commands description.

Command Description
RESET Reset the codec. Forces reinitialization of the GStreamer pipeline on the next transcode call.
MAKE_KEY_FRAME Not supported by the VCodecGst library.

Build and connect to your project

Before compiling, you must install GStreamer packages for your system. CMake finds GStreamer by pkg-config (modules gstreamer-1.0, gstreamer-base-1.0, gstreamer-video-1.0 and gstreamer-app-1.0). Packages to build the library on Ubuntu:

sudo apt-get install pkg-config libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

The GStreamer elements of the selected TYPE (see Table 1 and Table 3) are needed at run time:

  • All types: gstreamer1.0-plugins-base (appsrc, appsink, videoconvert) and gstreamer1.0-plugins-bad (h264parse, h265parse).
  • TYPE 0 (software): gstreamer1.0-plugins-ugly (x264enc), gstreamer1.0-plugins-bad (x265enc), gstreamer1.0-libav (avdec_h264, avdec_h265) and gstreamer1.0-plugins-good (jpegenc, jpegdec).
  • TYPE 1 (Intel): gstreamer1.0-vaapi (vaapih264enc, vaapih265enc, vaapih264dec, vaapih265dec), a VA-API driver (for example intel-media-va-driver-non-free) and gstreamer1.0-plugins-good (jpegenc, jpegdec).
  • TYPE 3 (NXP) and TYPE 4 (V4L2): gstreamer1.0-plugins-good (jpegenc, jpegdec, and v4l2h264enc, v4l2h265enc, v4l2h264dec, v4l2h265dec for TYPE 4).
  • TYPE 2 (Nvidia Jetson), TYPE 3 (NXP) and TYPE 5 (Rockchip): the GStreamer plugins of the hardware are part of the BSP of the board (JetPack, NXP BSP, Rockchip BSP).

Install command of the plugins of TYPE 0, 1 and 4 on Ubuntu (gstreamer1.0-tools provides gst-inspect-1.0 to check the installed elements):

sudo apt-get install gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-vaapi intel-media-va-driver-non-free gstreamer1.0-tools

GStreamer is LGPL 2.1 or later, but every plugin and the library which it wraps has its own license (for example x264enc of gst-plugins-ugly and x265enc are GPL, gst-libav follows the license of the FFmpeg build, vendor plugins follow the license of their BSP). The user of the library has to check and control the licenses of the installed GStreamer plugins (see GStreamer licensing).

Windows (not tested): the GStreamer development package for MSVC contains the pkg-config files of GStreamer in the lib/pkgconfig folder of the installation. CMake finds GStreamer if a pkg-config program is in the PATH and the PKG_CONFIG_PATH environment variable contains this folder (for example C:\gstreamer\1.0\msvc_x86_64\lib\pkgconfig).

Build stand alone

The repository does not contain sub-repositories. The libraries VCodecGst depends on (Frame, VCodec, Logger) must be cloned into the same folder as VCodecGst. The example also needs the FormatConverter and SimpleUI libraries. The folder structure must be:

<work folder>
    VCodecGst
    Frame
    VCodec
    Logger
    FormatConverter
    SimpleUI

Typical commands to build the library, the test application and the example (use the URLs of the repositories provided to you):

git clone <VCodecGst repository URL>
git clone <Frame repository URL>
git clone <VCodec repository URL>
git clone <Logger repository URL>
git clone <FormatConverter repository URL>
git clone <SimpleUI repository URL>
cd VCodecGst
mkdir build
cd build
cmake ..
make

On Windows use cmake --build . --config Release instead of make. If VCodecGst is the top-level CMake project, the main CMakeLists.txt file adds the folders ../Frame, ../VCodec and ../Logger (in this order) before the library itself and the folders ../FormatConverter and ../SimpleUI (in this order) before the example. If a folder does not exist, the CMake configuration fails with an error. The folders of the example are not needed if the example is not built (cmake .. -D_SUBMODULE_CACHE_OVERWRITE=OFF -D_VCODEC_GST_EXAMPLE=OFF). The test application and the example are built into the build/bin folder (build/bin/Release with a multi-configuration generator such as Visual Studio).

CMake options

Table 5 - CMake options of the repository. In the options below ${PARENT} is the prefix defined by the top-level project (empty if VCodecGst is the top-level project).

Option Default Description
${PARENT}_VCODEC_GST ON Build the library.
${PARENT}_VCODEC_GST_TEST ON (stand alone), OFF (included in other project) Build the test application.
${PARENT}_VCODEC_GST_EXAMPLE ON (stand alone), OFF (included in other project) Build the example (needs FormatConverter and SimpleUI).
${PARENT}_SUBMODULE_CACHE_OVERWRITE ON Forced rewriting of the options above in the CMake cache. The top-level project sets it to OFF when it includes VCodecGst as a sub-repository. To switch off the test application or the example of a stand alone build, set it to OFF too, for example: cmake -D_SUBMODULE_CACHE_OVERWRITE=OFF -D_VCODEC_GST_EXAMPLE=OFF ..
CMAKE_BUILD_TYPE Release (if not specified) Build type.
BUILD_SHARED_LIBS OFF (not defined) Standard CMake variable: ON builds the libraries as dynamic libraries.

Connect to your project

If you want to connect the VCodecGst library to your CMake project as source code, you can do the following. The VCodecGst library does not add its dependencies when it is included in another CMake project: your project must add the Frame, VCodec and Logger libraries. For example, if your repository has the structure:

CMakeLists.txt
src
    CMakeLists.txt
    yourLib.h
    yourLib.cpp

You can add the Frame, VCodec, Logger and VCodecGst repositories as sub-repositories with the following commands (use the URLs of the repositories provided to you):

cd <your repository folder>
git submodule add <Frame repository URL> 3rdparty/Frame
git submodule add <VCodec repository URL> 3rdparty/VCodec
git submodule add <Logger repository URL> 3rdparty/Logger
git submodule add <VCodecGst repository URL> 3rdparty/VCodecGst

In your repository folder, the 3rdparty/Frame, 3rdparty/VCodec, 3rdparty/Logger and 3rdparty/VCodecGst folders will be created. Alternatively, you can copy the repository folders to the 3rdparty folder of your repository. Create a CMakeLists.txt file in the 3rdparty folder. The CMakeLists.txt file should contain:

cmake_minimum_required(VERSION 3.13)

################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)

################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)

################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_FRAME                           ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_FRAME)
    SET(${PARENT}_FRAME                                 ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_FRAME_TEST                            OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_VCODEC                          ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_VCODEC)
    SET(${PARENT}_VCODEC                                ON  CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_LOGGER                          ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_LOGGER)
    SET(${PARENT}_LOGGER                                ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_LOGGER_EXAMPLE                        OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_VCODEC_GST                      ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_VCODEC_GST)
    SET(${PARENT}_VCODEC_GST                            ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_VCODEC_GST_TEST                       OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_VCODEC_GST_EXAMPLE                    OFF CACHE BOOL "" FORCE)
endif()

################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_FRAME)
    add_subdirectory(Frame)
endif()

if (${PARENT}_SUBMODULE_VCODEC)
    add_subdirectory(VCodec)
endif()

if (${PARENT}_SUBMODULE_LOGGER)
    add_subdirectory(Logger)
endif()

if (${PARENT}_SUBMODULE_VCODEC_GST)
    add_subdirectory(VCodecGst)
endif()

The 3rdparty/CMakeLists.txt file adds the Frame, VCodec, Logger and VCodecGst folders to your project (the dependencies first, then the library) and excludes the test applications and examples. The structure of your repository will be:

CMakeLists.txt
src
    CMakeLists.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    Frame
    VCodec
    Logger
    VCodecGst

Next, you need to include the 3rdparty folder in the main CMakeLists.txt file of your repository. Add the following line at the end of your main CMakeLists.txt:

add_subdirectory(3rdparty)

Next, you need to include the VCodecGst library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} VCodecGst)

If your project does not add the Frame, VCodec or Logger library, the compilation of the VCodecGst library fails because a header file is not found (for example, GCC reports “VCodec.h: No such file or directory”).

Done!

Simple example

This simple example generates the video frames itself (a ball moving over a background with a gradient and pseudo-random noise that changes every frame, drawn with the drawing functions of the SimpleUI library), converts the frames to the NV12 pixel format (required for the codec) with the FormatConverter library and encodes 200 frames with the H264 software encoder. Source code:

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <exception>
#include <iostream>
#include "FormatConverter.h"
#include "Frame.h"
#include "SimpleUi.h"
#include "VCodec.h"
#include "VCodecGst.h"



namespace
{
/// Size of the generated video frames, pixels (the codec needs even values).
constexpr int FRAME_WIDTH{1280};
constexpr int FRAME_HEIGHT{720};
/// Number of generated and encoded frames.
constexpr int NUM_FRAMES{200};
/// Number of consecutive encoding errors after which the example stops.
constexpr int MAX_ENCODING_ERRORS{10};
/// Radius of the moving ball, pixels.
constexpr int BALL_RADIUS{60};
/// Amplitude of the pseudo-random noise of the generated frames: every color
/// component changes by up to this value in both directions.
constexpr int NOISE_AMPLITUDE{6};
/// Number of the noise values from -NOISE_AMPLITUDE to NOISE_AMPLITUDE.
constexpr uint32_t NOISE_RANGE{static_cast<uint32_t>(2 * NOISE_AMPLITUDE + 1)};



/// Generator of a synthetic video in BGR24 format: a background with a
/// gradient, a ball that moves over it and bounces at the frame borders and
/// pseudo-random noise that changes every frame, like the noise of a camera
/// sensor.
class VideoGenerator
{
public:

    /// Class constructor. Draws the background.
    VideoGenerator()
    {
        if (m_background.data == nullptr)
        {
            return;
        }
        // Vertical gradient from light blue to dark green with soft stripes.
        uint8_t* pixel = m_background.data;
        for (int y = 0; y < FRAME_HEIGHT; ++y)
        {
            const int t = y * 255 / FRAME_HEIGHT;
            for (int x = 0; x < FRAME_WIDTH; ++x)
            {
                const int stripe = (x / 64) % 2 * 8;
                pixel[0] = static_cast<uint8_t>(230 - t * 3 / 4);
                pixel[1] = static_cast<uint8_t>(170 - t / 4 + stripe);
                pixel[2] = static_cast<uint8_t>(120 - t / 3);
                pixel += 3;
            }
        }
    }



    /// Check that the frames were allocated.
    bool isValid() const
    {
        return m_background.data != nullptr && m_frame.data != nullptr;
    }



    /// Draw the next frame: the background, the ball at its next position
    /// and new noise.
    rapidpixel::Frame& next()
    {
        std::memcpy(m_frame.data, m_background.data,
                    static_cast<size_t>(m_frame.size));

        // The ball moves along a straight line and bounces at the borders.
        m_x += m_velocityX;
        m_y += m_velocityY;
        if (m_x < BALL_RADIUS || m_x >= FRAME_WIDTH - BALL_RADIUS)
        {
            m_velocityX = -m_velocityX;
        }
        if (m_y < BALL_RADIUS || m_y >= FRAME_HEIGHT - BALL_RADIUS)
        {
            m_velocityY = -m_velocityY;
        }
        rapidpixel::SimpleUiDraw::fillCircle(m_frame, m_x, m_y, BALL_RADIUS,
                                             rapidpixel::Color{220, 70, 40, 255});

        // Noise: every color component changes by a pseudo-random value.
        uint8_t* data = m_frame.data;
        const size_t size = static_cast<size_t>(m_frame.size);
        for (size_t i = 0; i < size; ++i)
        {
            m_state ^= m_state << 13;
            m_state ^= m_state >> 17;
            m_state ^= m_state << 5;
            const int noise = static_cast<int>(m_state % NOISE_RANGE) - NOISE_AMPLITUDE;
            const int value = data[i] + noise;
            data[i] = static_cast<uint8_t>(std::clamp(value, 0, 255));
        }
        return m_frame;
    }

private:

    /// Background of the frames.
    rapidpixel::Frame m_background{FRAME_WIDTH, FRAME_HEIGHT,
                                   rapidpixel::Fourcc::BGR24};
    /// Current frame.
    rapidpixel::Frame m_frame{FRAME_WIDTH, FRAME_HEIGHT,
                              rapidpixel::Fourcc::BGR24};
    /// Center of the ball, pixels.
    int m_x{FRAME_WIDTH / 3};
    int m_y{FRAME_HEIGHT / 2};
    /// Velocity of the ball, pixels per frame.
    int m_velocityX{9};
    int m_velocityY{5};
    /// State of the pseudo-random generator (xorshift).
    uint32_t m_state{0x9E3779B9U};
};
}



int main()
{
    try
    {
        // Create codec object and set params.
        rapidpixel::VCodecGst encoder;
        if (!encoder.setParam(rapidpixel::VCodecParam::BITRATE_KBPS, 10000) ||
            !encoder.setParam(rapidpixel::VCodecParam::FPS, 20) ||
            !encoder.setParam(rapidpixel::VCodecParam::GOP, 30) ||
            !encoder.setParam(rapidpixel::VCodecParam::H264_PROFILE, 0))
        {
            std::cout << "Can't set codec params" << std::endl;
            return -1;
        }

        // Video generator and frames. The converter makes NV12 frames
        // (required for the codec) from the generated frames.
        VideoGenerator generator;
        rapidpixel::FormatConverter converter;
        rapidpixel::Frame nv12Frame;
        nv12Frame.fourcc = rapidpixel::Fourcc::NV12;
        rapidpixel::Frame h264Frame(FRAME_WIDTH, FRAME_HEIGHT,
                                    rapidpixel::Fourcc::H264);
        if (!generator.isValid() || h264Frame.data == nullptr)
        {
            std::cout << "Can't allocate frames" << std::endl;
            return -1;
        }

        // Main loop: until all frames are encoded or too many errors.
        int errors = 0;
        for (int i = 1; i <= NUM_FRAMES; ++i)
        {
            // Generate next video frame and convert it to NV12.
            if (!converter.convert(generator.next(), nv12Frame))
            {
                std::cout << "Can't convert frame" << std::endl;
                return -1;
            }
            nv12Frame.frameId = i; // Just to show unique info.

            // Encode data.
            if (!encoder.transcode(nv12Frame, h264Frame))
            {
                std::cout << "Can't encode frame" << std::endl;
                if (++errors >= MAX_ENCODING_ERRORS)
                {
                    return -1;
                }
                continue;
            }
            errors = 0;

            // Show info.
            std::cout << "[" << h264Frame.frameId << "] Size " <<
                         h264Frame.size << " Compression ratio : %" <<
                         static_cast<int64_t>(h264Frame.size) * 100 /
                         nv12Frame.size << std::endl;
        }
        std::cout << "Done" << std::endl;
    }
    catch (const std::exception& e)
    {
        // The frames can not be drawn or there is not enough memory.
        std::cout << "Error: " << e.what() << std::endl;
        return -1;
    }

    return 0;
}

Test application

The test application (VCodecGst/test/main.cpp) checks the VCodecGst C++ library with the software codecs: parameters and commands, encoding and decoding of JPEG, H264 and HEVC frames (luma PSNR of the decoded frames against the source frames), the parameter sets of HEVC frames (VPS, SPS and PPS once in the first frame and in every key frame), frames with a width that is not a multiple of 4, invalid frames, an H264 access unit with an end of sequence NAL unit, damaged data, the creation of the GStreamer pipeline again after the RESET command and parameter changes, encoding and decoding with one object, the hardware platform types (their GStreamer elements may be missing: the calls must return in time) and calls from several threads. The application needs the GStreamer elements x264enc, x265enc, jpegenc, avdec_h264, avdec_h265, jpegdec, h264parse, h265parse, videoconvert, appsrc and appsink (see the packages in Build and connect to your project). It writes no files and returns 0 if all tests passed. To run the application, execute the following commands on Linux:

cd <application folder>
./VCodecGstTest

Output of the application:

=================================================
VCodecGst v1.0.3 test
=================================================

Version:
OK

Default parameters:
OK

Set parameters:
OK

Invalid parameters:
OK

Commands:
OK

JPEG encoding and decoding:
OK

H264 encoding and decoding:
OK

HEVC encoding and decoding:
OK

HEVC parameter sets:
OK

Width not multiple of 4:
OK

Invalid frames to encode:
OK

Invalid frames to decode:
OK

End of sequence in H264 data:
OK

Damaged data:
Damaged frames decoded: 14 / 40
Damaged frames decoded: 35 / 40
OK

Pipeline re-creation:
OK

Encoding and decoding with one object:
OK

Hardware platform types:
Type 1: failed, failed
Type 2: failed, failed
Type 3: failed, failed
Type 4: failed, failed
Type 5: failed, failed
OK

Multithreading:
OK

All tests passed

Table of contents