rtp_pusher_web_logo

RtpPusher C++ library

v1.6.3

Table of contents

Overview

RtpPusher is a C++ library that provides H.264 RTP, H.265 RTP, and JPEG RTP packet transmission over the network. The library internally handles NAL unit extraction and fragmentation into UDP packets. It includes an internal frame buffer (16 frames) to handle temporary bandwidth constraints on low-bitrate channels, preventing frame loss when large key frames take extended time to transmit. The library depends on the Frame library (provides the video frame object declaration, source code included, Apache 2.0 license) and the UdpSocket library (provides methods to work with UDP sockets). The example application also depends on the VSourceFile library to read H.264/H.265/JPEG frames from files (source code included). To prevent network overload, the library controls the output buffer size (if available) by pausing transmission when the buffer exceeds a threshold. If the buffer size is not available, the library regulates the time interval between RTP packets based on the configured bandwidth. The library has no third-party dependencies that need to be installed in the OS. The library is compatible with Linux and Windows.

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 15.07.2024 - First version.
1.1.0 12.12.2024 - Fix bugs for Windows OS.
- Update code structure.
- Update example.
1.1.1 03.04.2025 - VSourceFile submodule update for example application.
1.1.2 22.06.2025 - UdpSocket update.
1.2.0 25.07.2025 - Add H.265 support.
1.3.0 03.10.2025 - Fix H.264 streaming issues.
- Add user data streaming support.
1.4.0 17.10.2025 - Add JPEG support.
- Fix user data insertion to H.265.
1.4.1 24.10.2025 - Fix documentation mistake about the SDP file for H.265.
1.4.2 12.11.2025 - Fix RTP packet calculation.
1.5.0 27.12.2025 - Add Muxed mode for user data transmission.
1.6.0 16.01.2026 - Added input frame buffering.
1.6.1 02.02.2026 - Reduced amount of allocated memory.
1.6.2 03.02.2026 - Fix memory allocation.
1.6.3 06.02.2026 - Added network overload control mechanism based on output buffer size monitoring.

Library files

The library is supplied as source code only. The user will 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.
3rdparty ------------------- Folder with third-party libraries.
    CMakeLists.txt --------- CMake file to include third-party libraries.
    Frame ------------------ Folder with Frame library source code.
    UdpSocket -------------- Folder with UdpSocket library source code.
src ------------------------ Folder with library source code.
    CMakeLists.txt --------- CMake file of the library.
    RtpPusher.h ------------ Main library header file.
    RtpPusher.cpp ---------- C++ implementation file.
    RtpPusherVersion.h ----- Header file with library version.
    RtpPusherVersion.h.in -- CMake service file to generate version header.
example -------------------- Folder with test application files.
    CMakeLists.txt --------- CMake file for example application.
    main.cpp --------------- Source C++ file of example application.
    3rdparty --------------- Folder with third-party libraries.
        CMakeLists.txt ----- CMake file to include third-party libraries.
        VSourceFile -------- Folder with VSourceFile library source code.

Internal architecture

The RtpPusher library uses a producer-consumer architecture with a circular buffer to decouple frame submission from network transmission:

┌─────────────────┐      ┌──────────────────────┐      ┌─────────────────┐
│  Application    │      │   Circular Buffer    │      │ Internal Thread │
│                 │      │    (16 frames)       │      │                 │
│  sendFrame() ───┼──────▶  [F1][F2][F3]...    ─┼──────▶  RTP Packets   │
│  (non-blocking) │      │                      │      │  over UDP       │
└─────────────────┘      └──────────────────────┘      └─────────────────┘
        │                         │                           │
        │                         │                           │
    Captures                  FIFO order               Bandwidth-controlled
    timestamp                 preserved                transmission

Key design features:

  1. Non-blocking API: The sendFrame() method copies frame data to the internal buffer and returns immediately, allowing the application to continue without waiting for network transmission.
  2. Frame buffering: A circular buffer holds up to 16 frames. This is critical for low-bitrate channels where transmitting a large key frame (e.g., I-frame) may take >100 ms while new frames continue arriving.
  3. Timestamp preservation: RTP timestamps are captured when sendFrame() is called, not when transmission begins. This ensures correct timing information even when frames are queued.
  4. Overload prevention: The library prevents network overload by controlling the output buffer size (if available) or by regulating the time interval between RTP packets based on the configured bandwidth.
  5. Overflow handling: When the buffer is full, the oldest frame is dropped to make room for new frames (FIFO with drop-oldest policy).

RtpPusher class description

RtpPusher class declaration

The RtpPusher.h file contains the RtpPusher class declaration:

namespace cr
{
namespace rtp
{
class RtpPusher
{
public:

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

    /// Class constructor.
    RtpPusher();

    /// Class destructor.
    ~RtpPusher();

    /// Initialize the video streamer with the specified parameters.
    bool init(std::string ip, uint16_t port,
              int bandwidthKbps = 1000000, int maxPayloadSize = 1420, uint16_t metadataPort = 0);

    /// Get the initialization status.
    bool isInit();

    /// Close the video streamer and release resources.
    void close();

    /// Send a frame to the video streamer.
    bool sendFrame(cr::video::Frame& frame, uint8_t* userData = nullptr, size_t userDataSize = 0);
};
}
}

getVersion method

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

static std::string getVersion();

The method can be used without an RtpPusher class instance. Example:

std::cout << "RtpPusher version: " << RtpPusher::getVersion() << std::endl;

Console output:

RtpPusher version: 1.6.3

init method

The init(…) method initializes the RTP pusher. The method sets the destination IP address, port, channel bandwidth (optional) in Kbps, and maximum RTP packet payload size (optional). After initialization, the library starts an internal thread responsible for video streaming. Method declaration:

bool init(std::string ip, uint16_t port, int bandwidthKbps = 1000000, int maxPayloadSize = 1420, uint16_t metadataPort = 0);
Parameter Value
ip Destination IP address.
port Destination UDP port.
bandwidthKbps Bandwidth of the network in Kbps. Default value is 1000000 Kbps (1 Gbps). This parameter is used to calculate the time gap between RTP packets.
maxPayloadSize Maximum RTP payload size in bytes. Default is 1420. This parameter is used to adjust RTP packet size for different networks (make it lower to prevent filtering by routers).
metadataPort Port for metadata (KLV etc). Controls how user data is transmitted:
0 (default) - User data is embedded into SEI NAL units of the video stream (H.264/H.265 only). Max ~239 bytes for H.264.
Same as port - User data is sent as separate RTP packets on the same port with payload type 98.
Different from port - User data is sent over a separate RTP stream on the specified port.

Note: JPEG does not support SEI NAL units. Use metadataPort == port or a separate port for JPEG streams.

Returns: TRUE if the RTP pusher was initialized successfully, FALSE otherwise.

isInit method

The isInit() method returns the RTP pusher initialization status. Method declaration:

bool isInit();

Returns: TRUE if the RTP pusher is initialized, FALSE otherwise.

close method

The close() method closes the RTP pusher. The library stops the internal streaming thread and releases all resources. Any frames remaining in the buffer are discarded. Method declaration:

void close();

sendFrame method

The sendFrame(…) method sends a video frame. The method accepts H.264, H.265, or JPEG frames and transmits them to the destination IP and port via RTP packets. The method is non-blocking: it copies the frame data into an internal circular buffer (capacity: 16 frames) and returns immediately. The internal thread then processes frames from the buffer in FIFO order, handling NAL unit extraction, fragmentation, and bandwidth-controlled transmission.

Buffering behavior:

  • The library maintains an internal buffer of up to 16 frames to handle temporary bandwidth constraints.
  • On low-bitrate channels, large key frames may take significant time to transmit (e.g., >100 ms). During this time, new frames are queued in the buffer.
  • If the buffer becomes full, the oldest frame is dropped to make room for the new frame.
  • Each frame’s RTP timestamp is captured at the moment sendFrame() is called, ensuring accurate timing even when frames are buffered.

If user data is provided, it will be sent with the same timestamp as the frame, allowing the receiver to synchronize user data with the video frame. Method declaration:

bool sendFrame(cr::video::Frame &frame, uint8_t *userData = nullptr, size_t userDataSize = 0);
Parameter Description
frame Frame object with H.264, H.265, or JPEG fourcc.
userData Pointer to user data to be sent with the frame (optional). Size limits depend on the metadata transmission mode configured in the init method.
userDataSize Size of user data in bytes (optional). Size limits depend on the metadata transmission mode.

User data size limits by transmission mode:

Mode Max Size Notes
Embedded in SEI (metadataPort = 0) ~239 bytes (H.264), larger for H.265 JPEG not supported; user data is ignored.
Muxed (metadataPort == port) maxPayloadSize - 12 bytes User data must fit in a single RTP packet.
Separate port (metadataPort != port) maxPayloadSize - 12 bytes User data must fit in a single RTP packet.

Returns: TRUE if the frame was queued successfully, FALSE otherwise.

Build and connect to your project

Typical commands to build RtpPusher library:

cd RtpPusher
mkdir build
cd build
cmake ..
make

If you want to connect RtpPusher library to your CMake project as source code, you can do the following. For example, if your repository has structure:

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

Create a 3rdparty folder and copy the RtpPusher repository folder there. New structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    RtpPusher

Create a CMakeLists.txt file in the 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_RTP_PUSHER                      ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_RTP_PUSHER)
    SET(${PARENT}_RTP_PUSHER                            ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_RTP_PUSHER_EXAMPLE                    OFF CACHE BOOL "" FORCE)
endif()

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

The 3rdparty/CMakeLists.txt file adds the RtpPusher folder to your project and excludes the example from compiling (by default the example is excluded from compiling if RtpPusher is included as a sub-repository). The new structure of your repository will be:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    RtpPusher

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 have to include the RtpPusher library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} RtpPusher)

Done!

Example

A simple application showing how to use the RtpPusher library. The example includes the VSourceFile library to read H.264 files.

#include <iostream>
#include <thread>
#include <chrono>
#include "VSourceFile.h"
#include "RtpPusher.h"

int main()
{
    // Init video source.
    cr::video::VSourceFile source;
    std::string initString = "test.h264";
    if (!source.openVSource(initString)) // Default 1280x720, 30 FPS.
        return -1;
    
    std::cout << "Enter IP address: ";
    std::string ip = "127.0.0.1";
    std::cin >> ip;

    std::cout << "Enter port number: ";
    int port = 7032;
    std::cin >> port;

    // Init streamer.
    cr::rtp::RtpPusher rtpPusher;
    if (!rtpPusher.init(ip, port))
        return -1;

    // Main loop.
    cr::video::Frame sourceFrame;
    while (true)
    {
        // Get an H.264 frame from the video source. We wait for a new frame for 1 sec.
        if (!source.getFrame(sourceFrame, 1000))
            continue;

        // Send frame.
        if (!rtpPusher.sendFrame(sourceFrame))
            continue;

        std::cout << "Frame : " << sourceFrame.frameId << std::endl;

        // Wait to control the frame rate (30 FPS = ~33ms between frames).
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
    }
    return 1;
}

How to play RTP stream

To play an RTP stream with ffplay or VLC, you need to create an SDP file (examples for H.264, H.265, and JPEG are in the /static folder). Important: Replace the IP address and port in the SDP file with the values used in your application.

For H.264:

s=RTP H264 Stream
c=IN IP4 127.0.0.1
m=video 7032 RTP/AVP 96
a=rtpmap:96 H264/90000 

For H.265:

s=RTP H265 Stream
c=IN IP4 127.0.0.1
m=video 7032 RTP/AVP 96
a=rtpmap:96 H265/90000

For JPEG:

s=RTP JPEG Stream
c=IN IP4 127.0.0.1
m=video 7032 RTP/AVP 26
a=rtpmap:26 JPEG/90000

Please ensure that the IP address and port number in the SDP file match the ones used in your application.

Play stream with ffplay:

ffplay -protocol_whitelist file,udp,rtp -i h264.sdp

To play an RTP stream in VLC, open the SDP file (e.g., h264.sdp) in VLC.

How to extract user data from RTP stream

The method to extract user data depends on the metadata transmission mode configured in the init() method:

Extracting from Separate Port Mode

If user data is sent over a separate RTP stream (metadataPort is different from 0 and different from the video port), you can extract KLV data using gstreamer:

gst-launch-1.0 udpsrc port=7034 caps="application/x-rtp, media=application, encoding-name=SMPTE336M, clock-rate=90000, payload=98" ! rtpjitterbuffer ! rtpklvdepay ! fakesink dump=true

Where 7034 is the metadata port (metadataPort in init method), and 98 is the payload type for user data (defined in RtpPusher.cpp, default is 98).

Extracting from Muxed Mode

If user data is muxed on the same port as video (metadataPort == port), the metadata packets have payload type 98 while video packets have payload type 96 (or 26 for JPEG). You need to filter packets by payload type to separate them.

Extracting from Embedded SEI Mode

If user data is embedded into SEI NAL units (metadataPort = 0), extraction requires parsing the video stream’s SEI NAL units. The user data is stored in user_data_unregistered SEI messages (payload type 5) with the following UUID:

A9 B1 9F 13 44 46 EC 4D 8C BF 65 B1 E1 2D CF FD

You can use video processing libraries like FFmpeg or custom parsers to extract SEI data from H.264/H.265 streams.

Output Example (Separate Port Mode)

00000000 (0x7f9f24014d44): 06 0e 2b 34 01 01 01 01 01 05 02 00 00 00 00 00  ..+4............
00000010 (0x7f9f24014d54): 10 59 65 73 74 65 72 64 61 79 73 20 57 6f 72 6c  .Yesterdays Worl
00000020 (0x7f9f24014d64): 64                                               d               

Table of contents