v5.1.1
- Overview
- Versions
- Library files
- Supported pixel formats
- Frame class description
- Build and connect to your project
The Frame class describes a video frame; the other libraries of the SDK use it as their frame type. Main file Frame.h contains declaration of Frame class and Fourcc enum which describes pixel formats supported by Frame class. The library doesn't have any third party dependencies. It uses C++17 standard.
Frame 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 - Library versions.
| Version | Release date | What's new |
|---|---|---|
| 1.0.0 | 08.09.2020 | First Frame class version in VideoDataStructures repository. |
| 2.0.0 | 19.11.2020 | Transformation matrix added. |
| 3.0.0 | 20.12.2020 | Added NV12 pixel format. |
| 4.0.0 | 20.01.2023 | - Interface changed. - Added new pixel formats. - Added shared_ptr for frame data. - Added new methods for copy and compare data. - Added new method for serialization and deserialization frame data. - Tests changed. |
| 5.0.0 | 19.03.2023 | - shared_ptr replaced by normal pointer. - Documentation updated. |
| 5.0.1 | 29.05.2023 | - Pixel format description (NV12 and NV21) mistake fixed. |
| 5.0.2 | 22.06.2023 | - Repository made public. |
| 5.0.3 | 22.06.2023 | - Added build guide. |
| 5.0.4 | 06.07.2023 | - Documentation updated. |
| 5.0.5 | 12.11.2023 | - Fixed errors serialization/deserialization functions. |
| 5.0.6 | 14.12.2023 | - Memory leakage from "=" operator fixed. |
| 5.0.7 | 19.03.2024 | - Type of data fields changes from uint32_t to int. |
| 5.0.8 | 16.04.2024 | - Documentation updated. - Method signatures optimizes. |
| 5.0.9 | 05.07.2024 | - CMake updated. |
| 5.1.0 | 04.10.2026 | - Review of the class, errors fixed: - deserialize(...) could write beyond the allocated memory (the data size of the serialized data is larger than the pixel format needs), could free the memory twice (unknown pixel format) and used negative or overflowing sizes.- Copy operator and copy constructor: memory freed twice or used after it was freed (empty source, size 0, failed allocation), frames with a data size that does not match the pixel format (for example compressed frames without dimensions) were copied without data. - release() resets the pointer to data.- cloneTo(...) releases the memory of the destination frame (memory leak).- Compare operators and serialize(...) do not dereference a null data pointer.- Frame sizes are calculated in 64 bit, sizes that do not fit in an int give an empty frame. - The copy operator and deserialize(...) do not change the frame if the memory can not be allocated.- Version of the serialization format is independent of the library version (format 5.0, compatible with 5.0.x). - Interface not changed. - Tests extended. |
| 5.1.1 | 06.10.2026 | - deserialize(...): use of released memory fixed if the serialized data is a part of the memory of the frame (the old memory was released before the data was copied), overlapping copy fixed if the memory of the frame is reused.- deserialize(...) returns FALSE (does not throw std::bad_alloc) if the memory can not be allocated, as documented.- Copy operator: overlapping copy fixed if the data of the source is a part of the memory of the destination. - New memory is filled with zeros only behind the copied data. - MAKE_FOURCC_CODE macro uses static_cast.- Documentation: memory ownership and memory size rules. - Tests extended (aliasing, all pixel formats), test data without rand().- Interface and class layout not changed (compatible with 5.0.x). |
The library supplied by source code only. The user would be 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.
Frame.h ------------ Main library header file.
FrameVersion.h ----- Header file with library version.
FrameVersion.h.in -- CMake service file to generate version header.
Frame.cpp ---------- C++ implementation file.
test ------------------- Folder with test application.
CMakeLists.txt ----- CMake file of test application.
main.cpp ----------- Source C++ file of test application.The Frame.h file contains Fourcc enum which defines supported pixel formats. Fourcc enum declaration:
/// Macro to make FOURCC code.
#define MAKE_FOURCC_CODE(a,b,c,d) (static_cast<uint32_t>(((d)<<24)|((c)<<16)|((b)<<8)|(a)))
/**
* @brief FOURCC codes enum.
*/
enum class Fourcc
{
/// RGB 24bit pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-rgb.html#v4l2-pix-fmt-rgb24
RGB24 = MAKE_FOURCC_CODE('R', 'G', 'B', '3'),
/// BGR 24bit pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-rgb.html#v4l2-pix-fmt-bgr24
BGR24 = MAKE_FOURCC_CODE('B', 'G', 'R', '3'),
/// YUYV 16bits per pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-packed-yuv.html#v4l2-pix-fmt-yuyv
YUYV = MAKE_FOURCC_CODE('Y', 'U', 'Y', 'V'),
/// UYVY 16bits per pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-packed-yuv.html#v4l2-pix-fmt-vyuy
UYVY = MAKE_FOURCC_CODE('U', 'Y', 'V', 'Y'),
/// Grayscale 8bit.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-yuv-luma.html#v4l2-pix-fmt-grey
GRAY = MAKE_FOURCC_CODE('G', 'R', 'A', 'Y'),
/// YUV 24bit per pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-packed-yuv.html#v4l2-pix-fmt-yuv24
YUV24 = MAKE_FOURCC_CODE('Y', 'U', 'V', '3'),
/// NV12 pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-yuv-planar.html#v4l2-pix-fmt-nv12
NV12 = MAKE_FOURCC_CODE('N', 'V', '1', '2'),
/// NV21 pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-yuv-planar.html#v4l2-pix-fmt-nv21
NV21 = MAKE_FOURCC_CODE('N', 'V', '2', '1'),
/// YU12 (YUV420) - Planar pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-yuv-planar.html#v4l2-pix-fmt-yuv420
YU12 = MAKE_FOURCC_CODE('Y', 'U', '1', '2'),
/// YV12 (YVU420) - Planar pixel format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-yuv-planar.html#v4l2-pix-fmt-yuv420
YV12 = MAKE_FOURCC_CODE('Y', 'V', '1', '2'),
/// JPEG compressed format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-compressed.html#v4l2-pix-fmt-jpeg
JPEG = MAKE_FOURCC_CODE('J', 'P', 'E', 'G'),
/// H264 compressed format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-compressed.html#v4l2-pix-fmt-h264
H264 = MAKE_FOURCC_CODE('H', '2', '6', '4'),
/// HEVC compressed format.
/// https://docs.kernel.org/userspace-api/media/v4l/pixfmt-compressed.html#v4l2-pix-fmt-hevc
HEVC = MAKE_FOURCC_CODE('H', 'E', 'V', 'C')
};Table 2 - Bytes layout of supported pixel formats. Example of 4x4 pixels image.
Memory ownership. A frame owns the memory that the class allocated (constructor with parameters, copy-constructor, copy operator and deserialize(...)) and releases it in release() and in the destructor. A clone (cloneTo(...)) and a frame whose data pointer was set by the user do not own the data: the data must stay valid as long as the frame uses it. Do not assign the data pointer of a frame that owns memory (call release() first), otherwise the memory leaks.
Memory size. When the class allocates memory, it allocates max(size, pixel format size) bytes for the width, height and pixel format of the frame and fills the memory behind the copied data with zeros. The pixel format size is width x height x 3 (RGB24, BGR24, YUV24), width x (height + height / 2) (NV12, NV21, YU12, YV12), width x height x 2 (YUYV, UYVY), width x height (GRAY) or width x height x 4 (JPEG, H264, HEVC: maximum size of compressed data). Frames whose data size does not fit in an int are not supported (the constructor creates an empty frame, deserialize(...) returns FALSE). The size field is the number of valid bytes of the data.
Frame.h file contains Frame class declaration. Frame class declaration:
namespace rapidpixel
{
class Frame
{
public:
/// Get string of current class version.
static std::string getVersion();
/// Default class constructor.
Frame();
/// Class constructor with parameters.
Frame(int width, int height, Fourcc fourcc, int size = 0, uint8_t* data = nullptr);
/// Copy class constructor.
Frame(Frame& src);
/// Class destructor.
~Frame();
/// Operator "=". Operator makes full copy of data.
Frame& operator= (const Frame& src);
/// Operator "!=". Operator to compare two frame objects.
bool operator!= (Frame& src);
/// Operator "==". Operator to compare two frame objects.
bool operator== (Frame& src);
/// Clone data. Method copies frame and copy just pointer to data.
void cloneTo(Frame& dst);
/// Release frame memory.
void release();
/// Serialize frame data.
void serialize(uint8_t* data, int& size);
/// Deserialize data to frame object.
bool deserialize(uint8_t* data, int size);
/// Frame width (pixels).
int width{0};
/// Frame height (pixels).
int height{0};
/// FOURCC code of data format.
Fourcc fourcc{Fourcc::YUV24};
/// Frame data size (bytes).
int size{0};
/// ID of frame.
int frameId{0};
/// ID of video source.
int sourceId{0};
/// Pointer to frame data.
uint8_t* data{nullptr};
};
}Default Frame class constructor does nothing. It doesn't allocate memory. Constructor declaration:
Frame();Constructor with parameters allocates memory and initializes Frame attributes (size, pixels format etc.). By default allocated memory filled by 0 but if user provides pointer to frame data it will be copied to internal frame buffer. Constructor declaration:
Frame(int width, int height, Fourcc fourcc, int size = 0, uint8_t* data = nullptr);| Parameter | Description |
|---|---|
| width | Frame width. Must be > 0. |
| height | Frame height. Must be > 0. |
| fourcc | Pixel format according to Fourcc enum. |
| size | Optional parameter. Size of external frame data. If user wants to initialize frame data from another buffer it can be done by initializing parameter size and data. |
| data | Optional parameter. Pointer to external frame data to be copied. The data is copied only if size is not negative and not larger than the data size of the pixel format, otherwise the data is not copied and the frame is filled with zeros. |
Example of frame initialization:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
// Copy.
rapidpixel::Frame image2 = rapidpixel::Frame(640, 480, rapidpixel::Fourcc::YUV24);
// Init by external data. Data will be copied.
rapidpixel::Frame image3(640, 480, rapidpixel::Fourcc::BGR24, 921600, externalDataBuffer);
// Init frame attributes manually.
Frame image4;
image4.data = externalDataBuffer;
image4.width = 640;
image4.height = 480;
image4.size = image4.width * image4.height * 3;
image4.fourcc = rapidpixel::Fourcc::BGR24;
// Dynamic allocation.
rapidpixel::Frame* image5 = new rapidpixel::Frame(640, 480, rapidpixel::Fourcc::YUV24);Copy constructor copy frame data from other Frame class instance. Constructor declaration:
Frame(Frame& src);Example of frame initialization:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
// Copy-constructor.
rapidpixel::Frame image2(image1);
// Copy-constructor.
rapidpixel::Frame image3 = rapidpixel::Frame(image1);The getVersion() method returns string of current version of Frame class. Method declaration:
static std::string getVersion();Method can be used without Frame class instance. Example:
std::cout << "Frame class version: " << rapidpixel::Frame::getVersion() << std::endl;Console output:
Frame class version: 5.1.1Copy operator "=" intended to full copy of frame data. Operator copies frame data and frame attributes. If the destination frame has memory, the same dimensions and pixel format as the source and its data size is not smaller than the data size of the source, the data is copied into the existing memory (also if the destination does not own the memory, for example a clone: the data of the original frame changes then), otherwise new memory is allocated. The data of the source can be a part of the memory of the destination. If the memory can not be allocated the operator throws std::bad_alloc and the destination frame is not changed. Operator declaration:
Frame& operator= (const Frame& src);Example:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
// Make copy.
rapidpixel::Frame image2 = image1;The cloneTo(...) method designed to clone frame object without copy of data. Method copies frame attributes and initialize pointer to frame data without copy of data. The memory that the destination frame owned before the call is released. The destination frame does not own the data: it stays valid only as long as the source frame keeps its data. Method declaration:
void cloneTo(Frame& dst);| Parameter | Description |
|---|---|
| dst | Frame object for initialization. Method initialize only frame attributes and copies pointer to frame data. |
Example:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
// Clone.
rapidpixel::Frame image2;
image1.cloneTo(image2);Compare operator "==" compares data attributes and frame data of to Frame objects. Operator declaration:
bool operator== (Frame& src);Returns: TRUE if all attributes and data of two Frame objects are identical (or when comparing an object to itself) or FALSE if not.
Example:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
rapidpixel::Frame image2(640, 480, rapidpixel::Fourcc::RGB24);
// Compare.
if (image1 == image2)
std::cout << "Identical" << std::endl;
// Change data.
image1.data[0] = rand() % 255;
// Compare.
if (image1 == image2)
std::cout << "Identical" << std::endl;
else
std::cout << "Not identical" << std::endl;Compare operator "!=" compares data attributes and frame data of to Frame objects. Operator declaration:
bool operator!= (Frame& src);Returns: TRUE if attributes or data of two Frame objects are not identical or FALSE if they are identical (or when comparing an object to itself).
Example:
// Frame filled by 0.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
rapidpixel::Frame image2(640, 480, rapidpixel::Fourcc::RGB24);
// Compare.
if (image1 != image2)
std::cout << "Not identical" << std::endl;
else
std::cout << "Identical" << std::endl;
// Change data.
image1.data[0] = rand() % 255;
// Compare.
if (image1 != image2)
std::cout << "Not identical" << std::endl;
else
std::cout << "Identical" << std::endl;The release() method intended to release allocated memory and reset frame attributes (the pointer to data is reset too, also for a frame that does not own its data, for example a clone). Method declaration:
void release();Example:
// Create Frame object with memory allocation.
rapidpixel::Frame image1(640, 480, rapidpixel::Fourcc::RGB24);
// Release Frame object.
image1.release();The serialize(...) method intended for serialization of Frame object with data. Sometimes the user needs to serialize an object in order to transfer or write it somewhere. The method can not check the size of the buffer: it must be at least the frame data size + 26 bytes and must not overlap the data of the frame. The serialized data starts with the version of the serialization format (5.0 in all versions 5.0.x and 5.1.x of the class) followed by the width, height, FOURCC, data size, frame ID and source ID (4 bytes each, byte order of the platform) and the frame data. A frame without data is serialized without data. Method declaration:
void serialize(uint8_t* data, int& size);| Parameter | Description |
|---|---|
| data | Pointer to data buffer. |
| size | Size of serialized data (zero if nothing was written). |
Example:
// Init frames.
Frame srcFrame(640, 480, Fourcc::BGR24);
// Fill source frame.
for (int i = 0; i < srcFrame.size; ++i)
srcFrame.data[i] = (uint8_t)(rand() % 255);
// Serialize data.
uint8_t* data = new uint8_t[1920 * 1080 * 4];
int size = 0;
srcFrame.serialize(data, size);The deserialize(...) method intended for deserialization of Frame object. The method checks the version of the serialization format, the pixel format, the size of the frame and the size of the data. If the data is not valid or the memory can not be allocated the method returns FALSE and the frame is not changed. The memory of the frame is reused like in the copy operator. The serialized data can be a part of the memory of the frame. Untrusted serialized data can describe frames up to 2 GB (the maximum the class supports): check the width and height in the header (bytes 2-9) before if your application needs a lower limit. Method declaration:
bool deserialize(uint8_t* data, int size);| Parameter | Description |
|---|---|
| data | Pointer to data buffer. |
| size | Size of serialized data. |
Returns: TRUE of the data deserialized or FALSE if not.
Example:
// Init frames.
Frame srcFrame(640, 480, Fourcc::BGR24);
Frame dstFrame(1280, 720, Fourcc::YUV24);
// Fill source frame.
for (int i = 0; i < srcFrame.size; ++i)
srcFrame.data[i] = (uint8_t)(rand() % 255);
// Serialize data.
uint8_t* data = new uint8_t[1920 * 1080 * 4];
int size = 0;
srcFrame.serialize(data, size);
// Deserialize data.
if (!dstFrame.deserialize(data, size))
{
std::cout << "Data not deserialized" << std::endl;
return false;
}
// Compare atributes.
if (srcFrame.size != dstFrame.size)
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
if (srcFrame.width != dstFrame.width)
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
if (srcFrame.height != dstFrame.height)
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
if (srcFrame.fourcc != dstFrame.fourcc)
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
if (srcFrame.sourceId != dstFrame.sourceId)
{
cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
if (srcFrame.frameId != dstFrame.frameId)
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
// Compare frame data.
for (int i = 0; i < srcFrame.size; ++i)
{
if (srcFrame.data[i] != dstFrame.data[i])
{
std::cout << "[" << __LINE__ << "] " << __FILE__ << " : ERROR" << std::endl;
return false;
}
}Frame class public members declaration:
/// Frame width (pixels).
int width{0};
/// Frame height (pixels).
int height{0};
/// FOURCC code of data format.
Fourcc fourcc{Fourcc::YUV24};
/// Frame data size (bytes).
int size{0};
/// ID of frame.
int frameId{0};
/// ID of video source.
int sourceId{0};
/// Pointer to frame data.
uint8_t* data{nullptr};Table 3 - Frame class public members.
| Field | Description |
|---|---|
| width | Frame width. |
| height | Frame height. |
| fourcc | Fourcc code according to Fourcc enum declared in Frame.h file. |
| size | Size of frame data. |
| frameId | Frame ID. User defines this filed. |
| sourceId | Source ID. User defines this field. |
| data | Pointer to frame data. |
Typical commands to build Frame library:
git clone https://github.com/ConstantRobotics-Ltd/Frame.git
cd Frame
mkdir build
cd build
cmake ..
makeIf you want connect Frame 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.cppYou can add repository Frame as submodule by command:
cd <your repository folder>
git submodule add https://github.com/ConstantRobotics-Ltd/Frame.git 3rdparty/FrameIn you repository folder will be created folder 3rdparty/Frame which contains files of Frame repository. Or you can copy Frame repository folder directly to 3rdparty folder of your repository. New structure of your repository:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
FrameCreate 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()
################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_FRAME)
add_subdirectory(Frame)
endif()File 3rdparty/CMakeLists.txt adds folder Frame to your project and excludes test application (Frame class tests) from compiling (by default test application excluded from compiling if Frame repository used as sub-repository). Your repository new structure will be:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
FrameNext 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 Frame library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} Frame)Done!
