Skip to content
ConstantRobotics-LtdPublic

About

Frame class is basic class for other projects. Frame class describes video frame structure and provide methods for serialization/deserialization.

Topics

Resources

Stars

0 stars

Watchers

4 watching

Forks

Latest commit

 

History

47 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

frame_logo

Frame C++ class

v5.1.1

Table of contents

Overview

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.

Versions

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

Library files

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.

Supported pixel formats

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.

rgbRGB24 bgrBGR24
yuvYUV24 grayGRAY
yuyvYUYV uyvyUYVY
nv12NV12 nv21NV21
yu12YU12 yv12YV12

Frame class description

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 class declaration

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 constructor

Default Frame class constructor does nothing. It doesn't allocate memory. Constructor declaration:

Frame();

Constructor with parameters

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 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);

getVersion method

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

Copy operator

Copy 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;

cloneTo method

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 equal

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 not equal

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;

release method

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();

serialize method

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);

deserialize method

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

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.

Build and connect to your project

Typical commands to build Frame library:

git clone https://github.com/ConstantRobotics-Ltd/Frame.git
cd Frame
mkdir build
cd build
cmake ..
make

If 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.cpp

You can add repository Frame as submodule by command:

cd <your repository folder>
git submodule add https://github.com/ConstantRobotics-Ltd/Frame.git 3rdparty/Frame

In 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
    Frame

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()

################################################################################
## 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
    Frame

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 Frame library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} Frame)

Done!

About

Frame class is basic class for other projects. Frame class describes video frame structure and provide methods for serialization/deserialization.

Topics

Resources

Stars

0 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages