
VOutputFb C++ library
v1.1.1
Table of contents
- Overview
- Versions
- Library files
- VOutputFb class description
- Build and connect to your project
- Portability
- Simple example
- Test application
- RPI configuration
Overview
VOutputFb C++ library provides simple interface to write video frame to framebuffer in Linux OS. It depends only on the Frame library (describes video frame class and pixel formats). The library supports C++17 standard and works only on Linux with framebuffer API support.
VOutputFb 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 | 18.06.2024 | First version. |
| 1.1.0 | 19.07.2024 | - CMake updated. - Test application and example updated. - Frame submodule updated. |
| 1.1.1 | 15.10.2026 | - Git submodules removed: the Frame library must be placed next to the library folder (see Build and connect to your project). - CMake updated: compiler warnings, exploit mitigation flags, standard C++ only. - init(…): closes the device opened before, rejects frame buffers with unsupported color depth or size, closes the descriptor on every error path, opens the device with O_CLOEXEC. - write(…): checks the frame data size and the position of the frame in the frame buffer memory (a frame that does not fit is rejected). - close(): resets the object state (write(…) and getResolution(…) return FALSE after close(), repeated close() and the destructor after close() are safe). - Copy and move of the class objects are deleted. - Documentation, example and test application updated (the test application does not need a frame buffer device). |
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.
static --------------------- Folder with images of the documentation.
voutputfb_web_logo.png - Logo image.
src ------------------------ Folder with library source code.
CMakeLists.txt --------- CMake file of the library.
VOutputFb.h ------------ Main library header file.
VOutputFbVersion.h ----- Header file with library version.
VOutputFbVersion.h.in -- File for CMake to generate version header.
VOutputFb.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.
VOutputFb class description
The VOutputFb class is not thread-safe: methods of one object must not be called from several threads at the same time without external synchronization. The object owns the device descriptor and the memory mapping, so it can not be copied or moved.
VOutputFb class declaration
VOutputFb class declared in VOutputFb.h file. Class declaration:
namespace rapidpixel
{
/// Frame buffer video output class.
class VOutputFb
{
public:
/// Get library version.
static std::string getVersion();
/// Default constructor. The object is not initialized.
VOutputFb();
/// Class destructor.
~VOutputFb();
/// Copy and move are deleted: the object owns the device.
VOutputFb(const VOutputFb&) = delete;
VOutputFb(VOutputFb&&) = delete;
VOutputFb& operator=(const VOutputFb&) = delete;
VOutputFb& operator=(VOutputFb&&) = delete;
/// Initialize frame buffer.
bool init(std::string device = "/dev/fb0");
/// Write video frame to device.
bool write(Frame& frame);
/// Close device.
void close();
/// Get frame buffer width and height.
bool getResolution(int& width, int& height);
};
}
getVersion method
The getVersion() method returns string of current version of VOutputFb class. Method declaration:
static std::string getVersion();
Method can be used without VOutputFb class instance:
std::cout << "VOutputFb class version: " << rapidpixel::VOutputFb::getVersion();
Console output:
VOutputFb class version: 1.1.1
init method
The init(…) method initializes video output device. The method closes the device opened before (if any), opens the device file (default “/dev/fb0”), reads the screen information, checks the frame buffer format and maps the frame buffer into memory. The frame buffer must have 16 bits per pixel (a pixel is written in the 5-6-5 format: red in the five most significant bits, blue in the five least significant bits) or 24 bits per pixel (a pixel is written as B, G, R bytes). Width, height and virtual height of the frame buffer must be from 1 to 65536 pixels, virtual height must be not less than height. The size of a frame buffer line is assumed to be width multiplied by bytes per pixel.
bool init(std::string device = "/dev/fb0");
| Parameter | Value |
|---|---|
| device | Frame buffer device path. |
Returns: TRUE if the video device is initialized or FALSE if not (the device can not be opened, it is not a frame buffer device, the color depth is not 16 and not 24 bits per pixel or the size is not supported). If FALSE is returned the object is not initialized.
write method
The write(…) method writes video frame to video device. The method checks that the object is initialized, the input frame format (must be BGR24), the resolution (width and height must be equal to the frame buffer resolution) and the data (the data pointer must not be nullptr and the data size must be not less than width * height * 3 bytes). User should control resolution (actual resolution can be obtained with getResolution(…) method after initialization) of frame buffer and should put frame with appropriate resolution. If the frame buffer has 24 bits per pixel the frame data is copied to the frame buffer directly. If the frame buffer has 16 bits per pixel the method converts the frame from BGR24 to the 5-6-5 format and writes it with the offset (xoffset, yoffset) of the frame buffer; the frame must fit into the frame buffer memory with this offset. The method can not check that the data pointer of the frame is valid for the data size of the frame: this is the responsibility of the caller. The frame is not changed.
bool write(Frame& frame);
| Parameter | Value |
|---|---|
| frame | Frame class object. Only BGR24 format is supported. |
Returns: TRUE if the video frame is written to device or FALSE if not (the object is not initialized, wrong frame format, resolution or data size, no frame data, or the frame does not fit into the frame buffer memory). If FALSE is returned nothing is written to the frame buffer.
close method
The close() method closes output device: it unmaps the frame buffer from memory and closes the device. The method can be called at any time and several times (it has no effect if the object is not initialized). The class destructor calls it. After the call write(…) and getResolution(…) return FALSE. Method declaration:
void close();
getResolution method
The getResolution(…) method returns actual frame buffer resolution. It can be used only after initialization (init(…) method). Method declaration:
bool getResolution(int& width, int& height);
| Parameter | Value |
|---|---|
| width | Frame buffer width, pixels. |
| height | Frame buffer height, pixels. |
Returns: TRUE if resolution provided or FALSE if the object is not initialized (the output parameters are not changed).
Build and connect to your project
Build stand alone
The repository does not contain sub-repositories. The Frame library that VOutputFb depends on must be cloned into the same folder as VOutputFb. The folder structure must be:
<work folder>
VOutputFb
Frame
Typical commands to build the library, the test application and the example (use the URLs of the repositories provided to you):
git clone <VOutputFb repository URL>
git clone <Frame repository URL>
cd VOutputFb
mkdir build
cd build
cmake ..
make
If VOutputFb is the top-level CMake project, the main CMakeLists.txt file adds the folder ../Frame before the library itself. If the folder does not exist CMake stops with an error. The library works only on Linux: CMake stops with an error on other operating systems.
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 VOutputFb is the top-level project).
| Option | Default | Description |
|---|---|---|
| ${PARENT}_VOUTPUT_FB | ON | Build the library. |
| ${PARENT}_VOUTPUT_FB_TEST | ON (stand alone), OFF (included in other project) | Build the test application. |
| ${PARENT}_VOUTPUT_FB_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 VOutputFb as a sub-repository. |
| CMAKE_BUILD_TYPE | Release (if not specified) | Build type. |
Connect to your project
If you want to connect the VOutputFb 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 and VOutputFb to the 3rdparty folder. The structure of your repository becomes:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
Frame
VOutputFb
The VOutputFb library does not add the Frame library when it is included in another project: your project must add the Frame library. Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain (the libraries are added in the order of their dependencies):
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_VOUTPUT_FB ON CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_VOUTPUT_FB)
SET(${PARENT}_VOUTPUT_FB ON CACHE BOOL "" FORCE)
SET(${PARENT}_VOUTPUT_FB_TEST OFF CACHE BOOL "" FORCE)
SET(${PARENT}_VOUTPUT_FB_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_VOUTPUT_FB)
add_subdirectory(VOutputFb)
endif()
File 3rdparty/CMakeLists.txt adds folders Frame and VOutputFb to your project and excludes test applications (Frame and VOutputFb class test applications) and the example from compiling. The structure of your repository will be:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
Frame
VOutputFb
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 VOutputFb library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} VOutputFb)
If the Frame library is not added by your project the compilation stops with the error “Frame.h: No such file or directory”.
Portability
The library works only on Linux (frame buffer API of the Linux kernel) and requires a C++17 compiler. It does not contain SIMD code and does not depend on the processor architecture. A 16-bit pixel is written to the frame buffer as a 16-bit value in the byte order of the processor. The library is built and tested with GCC 12 and 13 and Clang 15 and 18 for x86-64 and aarch64 (Release and Debug).
Simple example
Below is the code for a simple application for streaming generated video frames to the /dev/fb0 frame buffer device. The application stops if the device does not accept 100 frames in a row. The code is the example/main.cpp file of the repository.
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <exception>
#include <iostream>
#include <thread>
#include "Frame.h"
#include "VOutputFb.h"
namespace
{
/// Number of failed attempts to write a frame in a row after which the example
/// stops.
constexpr int MAX_WRITE_FAILURES{100};
/// Period of the output frames, milliseconds.
constexpr int FRAME_PERIOD_MS{33};
/// Stream generated frames to the frame buffer device until the device
/// stops accepting frames.
int streamFrames()
{
// Open video device.
rapidpixel::VOutputFb videoOutput;
if (!videoOutput.init("/dev/fb0"))
{
std::cerr << "Can't open frame buffer device" << std::endl;
return -1;
}
// Get frame buffer resolution.
int width{0};
int height{0};
if (!videoOutput.getResolution(width, height))
{
std::cerr << "Can't get frame buffer resolution" << std::endl;
return -1;
}
std::cout << "Resolution: " << width << "x" << height << std::endl;
// Create BGR frame with certain resolution which must be equal to the
// frame buffer resolution. The constructor throws std::bad_alloc if the
// memory can not be allocated.
rapidpixel::Frame frame(width, height, rapidpixel::Fourcc::BGR24);
if (frame.data == nullptr)
{
std::cerr << "Can't create frame" << std::endl;
return -1;
}
// Main loop.
uint8_t value = 0;
int failures = 0;
while (failures < MAX_WRITE_FAILURES)
{
// Fill image by color.
std::memset(frame.data, value, static_cast<size_t>(frame.size));
++value;
// Write frame to device.
if (videoOutput.write(frame))
{
failures = 0;
}
else
{
std::cerr << "Can't write frame" << std::endl;
++failures;
}
std::this_thread::sleep_for(std::chrono::milliseconds(FRAME_PERIOD_MS));
}
return -1;
}
}
int main()
{
try
{
return streamFrames();
}
catch (const std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return -1;
}
}
Test application
VOutputFb/test folder contains the test application. The application does not need a frame buffer device: it uses a fake frame buffer (a memory file) and checks init(…), write(…), close() and getResolution(…) for 16 and 24 bits per pixel: the version string, a not initialized object, devices that are not frame buffers, unsupported screen information, the exact content of the frame buffer memory after writing frames (pixel conversion, frame buffer offsets, nothing is written outside of the frame), rejected frames, repeated use after close(), the routine that writes frames with a moving square to a device and a campaign with 20000 random screen information and frames. The application prints one line per test and returns 0 if all tests passed, otherwise it returns 1. The application writes no files. Test application output will look like this:
VOutputFb v1.1.1 test
Version: OK
Not initialized: OK
Init failures: OK
Unsupported screen information: OK
Init: OK
Write 24 bit: OK
Write 16 bit: OK
Rejected frames: OK
After close: OK
Device routine: OK
Random campaign: (5544 accepted, 2225 written) OK
All tests passed
If the path of a real frame buffer device is passed as the first argument (for example bin/VOutputFbTest /dev/fb0), the application additionally writes 100 frames with a moving square to this device:
Device /dev/fb0: (resolution 1280x720) OK
RPI configuration
This section describes set up process for Raspberry PI Zero 2W single board computer. In order to obtain composite video output on Raspberry Pi Zero 2W, you need to add the following lines to the /boot/config.txt (or /boot/firmware/config.txt if you have Bookworm OS) file:
-
Composite PAL:
dtoverlay=vc4-kms-v3d,composite sdtv_mode=2 sdtv_aspect=1 hdmi_force_hotplug=0 hdmi_ignore_hotplug=1 enable_tvout=1Ensure that you do not have multiple dtoverlay=vc4-kms-v3d lines.
And append following line to kernel command line in /boot/cmdline.txt (or /boot/firmware/cmdline.txt if you have Bookworm Debian OS) file (before other content):
video=Composite-1:720x576-24@25 vc4.tv_norm=PAL -
HDMI PAL RESOLUTION:
only kernel command line in /boot/cmdline.txt (or /boot/firmware/cmdline.txt if you have Bookworm Debian OS) file:
vc4.tv_norm=PAL video=HDMI-A-1:720x576M-24@60D
If you want to use different resolution, you can just change the resolution in the video=Composite-1:720x576-24@25 or video=HDMI-A-1:720x576M-24@60D and remove vc4.tv_norm=PAL. After that, you need to reboot the Raspberry Pi Zero 2W.