voutputv4l2_web_logo

VOutputV4L2 C++ library

v1.0.7

Table of contents

Overview

VOutputV4L2 C++ library provides interface to write video frame to V4L2 video devices in Linux OS. An example of a video device is v4l2loopback device (virtual video devices). Normal (v4l2) applications will read these devices as if they were ordinary video devices, but the video will not be read from e.g. a capture card but instead it is generated by user’s application. Using the library user’s application can be as video source for third-party processes. The library provides simple interface. It depends on Frame library (describes video frame structure and pixel formats) and Logger library (provides functions to write logs). The repository does not contain the source code of these libraries: they must be placed next to the repository (see Build stand alone). The library supports C++17 standard and works only on Linux with V4L2 API support. The test application checks the library without a video device (opening of names that are not V4L2 output devices, writing without a device and of invalid frames, repeated close calls, file descriptors after failed opens) and then writes generated video to a video device until it is stopped (Ctrl+C) or 100 frames in a row are not written. The video is a scene with gradients, an object that moves over it and bounces at the frame borders and pseudo-random noise that changes every frame, with the frame number on every frame. The frames are drawn with the SimpleUI library and converted to YUYV with the FormatConverter library. The frame rate, the frame size, the pixel format (BGR24 or YUYV) and the device name are entered at start (by default 30 FPS, 1280x720, BGR24 and /dev/video0).

VOutputV4L2 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.

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 10.08.2023 First version.
1.0.1 09.01.2024 - Examples updated.
- Code optimized.
- Documentation updated.
1.0.2 25.04.2024 - Submodules updated.
- Documentation updated.
1.0.3 17.05.2024 - Documentation updated.
1.0.4 18.07.2024 - CMake structure updated.
- Submodules updated.
1.0.5 03.04.2025 - Logger submodule update.
1.0.6 15.10.2026 - Sub-repositories (Frame and Logger libraries) removed: the libraries are expected next to the repository.
- Memory overflow fixed: a frame that is larger than a buffer of the device is rejected. Frames without data and frames with zero or negative width, height or size are rejected.
- The video stream is stopped and the buffers are released before the device is configured for another frame format, so the frame size and pixel format can be changed while streaming. Buffers are unmapped and the device file is closed (once) by the close() method. The device can be opened again.
- The device file is opened once with the O_CLOEXEC flag and checked after opening. Empty device names and names with a zero character are rejected.
- Copying of the class object is deleted.
- Failures of the device configuration release the allocated resources.
- Strict compiler warnings and exploit mitigation compiler and linker flags added to the CMake files.
- API checks without a video device added to the test application. The example and the test application limit the number of errors in a row.
- Documentation and code comments updated.
1.0.7 25.10.2026 - Test application: the video frames are generated (an object moving over a background with noise) instead of being read from the file test.mp4, OpenCV is not used any more.
- Compiler warning in 32-bit builds fixed.

Library files

The library is supplied only by source code. The user is given 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.
    VOutputV4L2.h ------------ Main library header file.
    VOutputV4L2Version.h ----- Header file with library version.
    VOutputV4L2Version.h.in -- File for CMake to generate version header.
    VOutputV4L2.cpp ---------- C++ implementation file.
test ------------------------- Folder for test application files.
    CMakeLists.txt ----------- CMake file for test application.
    main.cpp ----------------- Source C++ file of test application.
example ---------------------- Folder for example application files.
    CMakeLists.txt ----------- CMake file for example application.
    main.cpp ----------------- Source C++ file of example application.
static ----------------------- Folder with the logo of the library.

VOutputV4L2 class description

VOutputV4L2 class declaration

VOutputV4L2 class declared in VOutputV4L2.h file. The class is not thread-safe: an object must not be accessed from several threads at the same time without external synchronization. Different objects are independent. The object owns the device file descriptor and the mapped buffers, so it can not be copied or moved. Class declaration:

namespace rapidpixel
{
/// V4L2 video output device class.
class VOutputV4L2
{
public:

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

    /// Class constructor.
    VOutputV4L2();

    /// Class destructor.
    ~VOutputV4L2();

    /// Copy and move are not supported.
    VOutputV4L2(const VOutputV4L2& src) = delete;
    VOutputV4L2(VOutputV4L2&& src) = delete;
    VOutputV4L2& operator= (const VOutputV4L2& src) = delete;
    VOutputV4L2& operator= (VOutputV4L2&& src) = delete;

    /// Open device.
    bool open(std::string device);

    /// Write video frame to device.
    bool write(Frame& frame);

    /// Set log level.
    void setLogLevel(rapidpixel::PrintFlag flag);

    /// Close device.
    void close(void);
};
}

getVersion method

The getVersion() method returns string of current version of VOutputV4L2 class. Method declaration:

static std::string getVersion();

Method can be used without VOutputV4L2 class instance:

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

Console output:

VOutputV4L2 class version: 1.0.7

open method

The open(…) method opens video output device and checks capabilities of the device. If the object has an open device, the device is closed first. The device file is opened with read and write access and must be a character device with video output and streaming capabilities (V4L2_CAP_VIDEO_OUTPUT and V4L2_CAP_STREAMING). Buffers are allocated and the video stream is turned on by the first call of the write(…) method. Method declaration:

bool open(std::string device);
Parameter Value
device Full V4L2 video device name. For example: “/dev/video4”. The name is used as is, so it must come from a trusted configuration. An empty name and a name with a zero character are rejected.

Returns: TRUE if the video device is open or FALSE if not (the name is wrong, the file can not be opened, the file is not a character device or the device has no video output and streaming capabilities). If FALSE is returned the object has no open device. The method does not throw exceptions.

write method

The write(…) method writes video frame to video device. The first video frame and every frame with another width, height or pixel format than the last written frame configure the device: the library stops the video stream, sets the frame format, allocates the buffers (two buffers are requested, the device can grant from 1 to 16) and turns the video stream on. If the device can not be configured, the next call configures it again. The frame is copied to a free buffer of the device. The method blocks until the device returns a free buffer. The frame object is not changed. Method declaration:

bool write(Frame& frame);
Parameter Value
frame Frame class object. Width and height must be greater than zero, data pointer must not be nullptr and data size must be greater than zero and not larger than the size of a buffer of the device (the device defines the buffer size). The library can not check that the data pointer points to the number of bytes in the data size field: the pointer and the size must be consistent. Supported formats (Fourcc enum of Frame class): RGB24, BGR24, YUYV, UYVY, GRAY, YUV24, NV12, NV21, YU12, YV12, HEVC, H264 and JPEG. Some devices may not support particular formats.

Returns: TRUE if the video frame is written to device or FALSE if not (the device is not open, the frame is not valid, the pixel format is not supported, the device can not be configured for the frame width, height and pixel format, the frame is larger than a buffer of the device or a request to the device failed). A rejected frame is not written; the device keeps working and the next valid frame can be written. The method does not throw exceptions.

setLogLevel method

The setLogLevel(…) method sets the log level of the object. By default logging is disabled (the library prints nothing). The library prints only status and error messages about the device and never prints frame data. Method declaration:

void setLogLevel(rapidpixel::PrintFlag flag);
Parameter Description
flag Logger flag which defined in Logger.h from Logger library:
DISABLE - disabled.
CONSOLE - print to console (terminal) only.
FILE - print to file only if save params was set.
CONSOLE_AND_FILE - print to file and console.

close method

The close() method stops the video stream, unmaps the buffers and closes the output device file. The method can be called several times and without a previous call of open(…). The destructor of the class calls the method. Method declaration:

void close();

Build and connect to your project

Build stand alone

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

<work folder>
    VOutputV4L2
    Frame
    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 <VOutputV4L2 repository URL>
git clone <Frame repository URL>
git clone <Logger repository URL>
git clone <FormatConverter repository URL>
git clone <SimpleUI repository URL>
cd VOutputV4L2
mkdir build
cd build
cmake ..
make

If VOutputV4L2 is the top-level CMake project, the main CMakeLists.txt file adds the folders ../Frame and ../Logger (in this order) before the library itself and the folders ../FormatConverter and ../SimpleUI (in this order) before the test application. If a folder does not exist CMake stops with an error. The folders of the test application are not needed if it is not built (cmake .. -D_SUBMODULE_CACHE_OVERWRITE=OFF -D_VOUTPUT_V4L2_TEST=OFF).

CMake options

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

Option Default Description
${PARENT}_VOUTPUT_V4L2 ON Build the library.
${PARENT}_VOUTPUT_V4L2_TEST ON (stand alone), OFF (included in other project) Build the test application (needs FormatConverter and SimpleUI).
${PARENT}_VOUTPUT_V4L2_EXAMPLE ON (stand alone), OFF (included in other project) Build the example.
${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 VOutputV4L2 as a sub-repository. To change the options above on the command line set it to OFF too, for example: cmake -D_SUBMODULE_CACHE_OVERWRITE=OFF -D_VOUTPUT_V4L2_TEST=OFF ..
CMAKE_BUILD_TYPE Release (if not specified) Build type.

Connect to your project

If you want to connect VOutputV4L2 library to your CMake project as source code you can make follow. For example, if your repository has structure:

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

Create folder 3rdparty in your repository. Copy repository folders Frame, Logger and VOutputV4L2 to 3rdparty folder. Structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    Frame
    Logger
    VOutputV4L2

Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt 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_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_VOUTPUT_V4L2                    ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_VOUTPUT_V4L2)
    SET(${PARENT}_VOUTPUT_V4L2                          ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_VOUTPUT_V4L2_TEST                     OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_VOUTPUT_V4L2_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_LOGGER)
    add_subdirectory(Logger)
endif()

if (${PARENT}_SUBMODULE_VOUTPUT_V4L2)
    add_subdirectory(VOutputV4L2)
endif()

File 3rdparty/CMakeLists.txt adds folders Frame, Logger and VOutputV4L2 to your project and excludes test applications and examples from compiling (by default test applications and examples are excluded from compiling if the libraries are included as sub-repositories). VOutputV4L2 does not add Frame and Logger when it is included in another project: the project must add them, in the order of their mutual dependencies, as the file above does. If the dependencies are not added, the compilation stops with the error Frame.h: No such file or directory. Structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    Frame
    Logger
    VOutputV4L2

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

add_subdirectory(3rdparty)

Next you have to include VOutputV4L2 library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} VOutputV4L2)

NOTE: The user that runs your application must have read and write access to the video device file.

Portability

The library works only on Linux (the CMake project stops with an error on other systems): it uses the V4L2 interface of the Linux kernel. The library has no code that depends on the CPU. Frame data is copied to the buffers of the device as a sequence of bytes and is not interpreted, so the byte order of the CPU does not matter. The library does not use SIMD instructions and has no options for them. The library, the example and the test application were built with GCC 12 and 13 and Clang 15 and 18 for x86-64, and the library and the example were built with GCC for aarch64.

Simple example

Below is the code for a simple application for streaming generated video frames to /dev/video4 device.

#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <thread>
#include "Frame.h"
#include "VOutputV4L2.h"



/// Name of the video output device. It must be a V4L2 output device, for
/// example a v4l2loopback device.
constexpr const char* DEVICE_NAME{"/dev/video4"};
/// Frame width, pixels.
constexpr int FRAME_WIDTH{640};
/// Frame height, pixels.
constexpr int FRAME_HEIGHT{480};
/// Delay between frames, ms (33 ms for 30 fps stream).
constexpr int FRAME_DELAY_MS{33};
/// Number of failed writes in a row after which the example stops.
constexpr int MAX_WRITE_ERRORS{100};



int main(void)
{
    // Open video device.
    rapidpixel::VOutputV4L2 videoOutput;
    if (!videoOutput.open(DEVICE_NAME))
    {
        std::cerr << "Can't open device " << DEVICE_NAME << std::endl;
        return -1;
    }

    // Create YUV frame with certain resolution.
    rapidpixel::Frame frame(FRAME_WIDTH, FRAME_HEIGHT,
                            rapidpixel::Fourcc::YUV24);
    if (frame.data == nullptr || frame.size <= 0)
    {
        std::cerr << "Can't create frame" << std::endl;
        return -1;
    }

    // Main loop.
    uint8_t value = 0;
    int errors = 0;
    while (errors < MAX_WRITE_ERRORS)
    {
        // Fill image by color.
        std::memset(frame.data, value++, static_cast<size_t>(frame.size));

        // Write frame to device.
        if (videoOutput.write(frame))
        {
            errors = 0;
        }
        else
        {
            std::cerr << "Can't write frame" << std::endl;
            ++errors;
        }

        // 33ms delay for 30 fps stream
        std::this_thread::sleep_for(std::chrono::milliseconds(FRAME_DELAY_MS));
    }

    std::cerr << "Too many write errors" << std::endl;
    return -1;
}

Table of contents