LibSourcey

LibSourcey

C++20 real-time media and networking toolkit. Joins FFmpeg, OpenCV, and WebRTC into a single async pipeline - capture devices, encode, transcode, and stream over the network in one elegant abstraction.

C++20libuvFFmpegOpenCVOpenSSL 3.xWebRTC

Most C++ media libraries solve one problem. FFmpeg handles codecs. OpenCV handles vision. libuv handles async IO. WebRTC handles peer-to-peer. Try to combine them and you’re knee-deep in memory ownership fights, callback hell across three different threading models, and build systems that take longer to debug than the actual code.

LibSourcey is the connective tissue. A modular C++20 toolkit that unifies real-time device capture, media encoding, computer vision processing, STUN/TURN/ICE signalling, and network streaming into a single coherent pipeline architecture. Capture from a camera, run it through an encoder, pipe it over a WebSocket - all async, all composable, no glue code.

We built it because we needed a way to ship real-time communication systems without treating every project as a bespoke integration exercise. The library has been running in production for over a decade.

The packet pipeline

The core abstraction is the PacketStream - a composable pipeline where sources emit packets, processors transform them, and sinks consume them. Everything is polymorphic through the IPacket interface, so a video frame, an audio buffer, and a network message all flow through the same machinery.

  Source              Processor              Sink
    |                    |                    |
  VideoCapture  -->  MultiplexEncoder  -->  TCP Socket
  AudioCapture  -->  (FFmpeg H.264+AAC) -->  HTTP Server
  FileReader    -->  VideoConverter     -->  WebSocket
    |                    |                    |
    +--- PacketStream (ordered, async, backpressure-aware) ---+
PacketStream stream;
stream.attachSource(videoCapture);
stream.attach(new av::MultiplexPacketEncoder(opts), 5);
stream.attach(socket, 10);
stream.start();

Sources emit. Processors transform. Sinks consume. Priority ordering controls the chain. The stream manages lifecycle - start, stop, pause, resume, error recovery - as a state machine. You don’t manage threads. You don’t manage memory. You describe the flow and the pipeline handles the rest.

This isn’t a toy abstraction. Backpressure propagates through the chain. Write queues have high water marks. Frame dropping kicks in under load. The pipeline handles the ugly parts so application code stays clean.

Media capture and encoding

The AV module wraps FFmpeg’s codec infrastructure in RAII types that don’t leak. VideoCapture and AudioCapture continuously emit decoded packets from system devices - cameras via V4L2, AVFoundation, or DirectShow depending on platform. MediaCapture handles file input with optional looping.

Encoding chains VideoEncoder and AudioEncoder into a MultiplexPacketEncoder that acts as a PacketStream processor. Define your format once:

av::EncoderOptions opts;
opts.oformat = av::Format{
    "MP4", "mp4",
    av::VideoCodec{"H.264", "libx264", 640, 480, 25, "yuv420p"},
    av::AudioCodec{"AAC", "aac", 2, 48000, 128000, "fltp"}
};

The encoder handles pixel format conversion (libswscale), PTS tracking, time base conversion, and codec flushing on close. All FFmpeg resources are wrapped in custom deleters - no manual av_frame_free() calls, no leaked contexts.

Compatible with FFmpeg 5, 6, and 7. The API adapts at compile time.

Networking on libuv

Every socket, timer, DNS lookup, and child process runs on libuv’s event loop. TCP, SSL, and UDP sockets share a common interface with chainable adapters for middleware-style extension. The SSL layer enforces TLS 1.2 minimum, supports ALPN negotiation, SNI, hostname verification, and session caching - all through OpenSSL 3.x’s EVP API.

The HTTP module implements client and server with WebSocket upgrade (RFC 6455), multipart forms, cookie management, and streaming responses. Built on llhttp for parsing - the same parser Node.js uses.

Socket.IO protocol v4 sits on top for real-time bidirectional messaging, which in turn supports Symple - our own presence and signalling protocol for WebRTC applications.

NAT traversal

Full RFC implementations of STUN (5389) and TURN (5766). Not thin wrappers - actual server and client implementations with:

  • STUN: Binding requests for NAT detection, transaction IDs, message integrity, fingerprint validation
  • TURN: Relay allocations (UDP and TCP), permission management, channel binding for fast-path data relay, configurable lifetime enforcement

Channel binding is the detail most TURN implementations skip. It replaces 36-byte STUN headers with 4-byte channel headers on the data path - significant when you’re relaying real-time media at scale.

Signal-driven architecture

The event system uses typed Signal<T> templates with attach() for callback registration. Thread-safe, multi-listener, and used everywhere - from socket events to stream state changes to installation progress. No virtual inheritance hierarchies. No observer pattern boilerplate. Just connect a lambda and go.

server.Connection += [](http::ServerConnection::Ptr conn) {
    conn->Payload += [](auto&, const MutableBuffer& buf) {
        // handle incoming data
    };
};

14 modules, zero bloat

Each module is self-contained with explicit dependencies. Enable what you need, skip what you don’t:

ModuleWhat it does
baseEvent loop, signals, packet streams, threading, timers, filesystem
cryptoHMAC, SHA-256/512, RSA, X509 - all OpenSSL 3.x EVP
netTCP/SSL/UDP sockets, DNS, chainable adapters
httpClient/server, WebSocket RFC 6455, forms, cookies, auth
jsonFast JSON wrapper
avFFmpeg codecs, device capture, encoding, format registry
socketioSocket.IO protocol v4
symplePresence + messaging for WebRTC signalling
stunRFC 5389 NAT detection
turnRFC 5766 relay server with channel binding
pacmPackage manager for plugin distribution
plugaDynamic plugin loading with ABI versioning
archoZIP extraction with path traversal protection
schedDeferred and periodic task scheduling

All external dependencies resolve through CMake FetchContent - libuv 1.50, llhttp 9.2.1, OpenSSL 3.x, nlohmann/json 3.11.3, minizip-ng, zlib 1.3.1. No system package requirements beyond a C++20 compiler.

Build and test

git clone https://github.com/sourcey/libsourcey.git
cd libsourcey
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
ctest --test-dir build

Enable specific modules:

cmake -B build \
  -DWITH_AV=ON \
  -DWITH_TURN=ON \
  -DWITH_OPENCV=ON \
  -DCMAKE_BUILD_TYPE=Release

CI runs GCC 12+, Clang 15+, AppleClang 15+, and MSVC 2022. ASan, TSan, and UBSan sanitizer builds catch the things unit tests miss. Docker multi-stage builds available for Ubuntu 24.04.