Skip to content

sourcey/icey

Repository files navigation

Icey

CI License: LGPL-2.1+

The C++ Media Stack

WebRTC, FFmpeg, and async networking in one toolkit. No Google monolith. No dependency hell. No fighting three build systems to get a frame on screen.

// The core of a WebRTC media server
PacketStream stream;
stream.attachSource(capture.get());
stream.attach(&session->media().videoSender(), 5);
stream.start();

Icey is the connective tissue: a modular C++20 toolkit that unifies FFmpeg, libuv, OpenSSL, and libdatachannel into a single composable pipeline. Capture, encode, transport, signalling, and relay. All dependencies managed via CMake FetchContent. Builds in minutes.

Documentation | Changelog | Contributing | LGPL-2.1+

Why Icey

libWebRTC (Google) libdatachannel GStreamer Icey
Build system GN/Ninja CMake Meson CMake
Build time Hours Minutes 30+ min Minutes
Binary size 50MB+ Small Large Small
SSL BoringSSL (conflicts) OpenSSL OpenSSL OpenSSL
Media codecs Bundled None GObject plugins FFmpeg (any codec)
Capture/encode Included No Plugin pipeline PacketStream pipeline
Signalling No No No Symple (built-in)
TURN server No No No RFC 5766 (built-in)
Language C++ C++17 C/GObject C++20

libdatachannel gives you the WebRTC transport pipe. Icey gives you the pipe, the water, and the faucet.

Architecture

Everything flows through PacketStream. Plug in a source, chain processors, attach a sink. The pipeline handles backpressure, frame dropping, and teardown so you don't. Nothing runs that you didn't ask for.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        PacketStream                             β”‚
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Source  │───▢│  Processor   │───▢│        Sink           β”‚  β”‚
β”‚  β”‚          β”‚    β”‚              β”‚    β”‚                       β”‚  β”‚
β”‚  β”‚ Camera   β”‚    β”‚ FFmpeg H.264 β”‚    β”‚ WebRTC Track Sender   β”‚  β”‚
β”‚  β”‚ File     β”‚    β”‚ Opus encode  β”‚    β”‚ Network socket        β”‚  β”‚
β”‚  β”‚ Network  β”‚    β”‚ OpenCV       β”‚    β”‚ File recorder         β”‚  β”‚
β”‚  β”‚ Device   β”‚    β”‚ Custom       β”‚    β”‚ HTTP response         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

WebRTC send path:
  MediaCapture β†’ VideoEncoder β†’ WebRtcTrackSender β†’ [libdatachannel]
                                                        β”‚
  Browser ◀── RTP/SRTP ◀── DTLS ◀── ICE (libjuice) β—€β”€β”€β”€β”˜
                                      β”‚
                              Icey TURN server
                              (relay for symmetric NATs)

WebRTC receive path:
  [libdatachannel] β†’ WebRtcTrackReceiver β†’ FFmpeg decode β†’ file/display
        β”‚
        └─── ICE β†’ DTLS β†’ SRTP decrypt β†’ RTP depacketise β†’ raw frames

Signalling (Symple v4):
  C++ server/client ◀──── WebSocket ────▢ Browser (symple-client-player)
  Auth, presence, rooms, call protocol (init/accept/offer/answer/candidate)

Camera to browser in 150 lines. Browser to file in 130. The pipeline handles the plumbing.

What You Can Build

Stream a webcam to any browser

150 lines of C++. Camera capture, H.264 encoding, WebRTC transport, Symple signalling. Open a browser, see video. No plugins, no Google, no pain.

// Accept call, wire up the pipeline, stream
session.IncomingCall += [&](const std::string& peerId) {
    session.accept();
};

session.StateChanged += [&](wrtc::PeerSession::State state) {
    if (state == wrtc::PeerSession::State::Active) {
        stream.attachSource(capture.get());
        stream.attach(&session->media().videoSender(), 5);
        stream.start();
    }
};

See src/webrtc/samples/webcam-streamer/ or read WebRTC in 150 Lines of C++.

Record a browser's camera server-side

Browser sends WebRTC, your C++ server decodes with FFmpeg, writes to any format. Video depositions, telehealth recording, proctoring - server-side recording without cloud vendor lock-in.

See src/webrtc/samples/media-recorder/.

Stream any video file to a browser

Feed an MP4 in, get a real-time WebRTC stream out. Data channel for seek commands. Build your own streaming service.

See src/webrtc/samples/file-streamer/.

Run your own TURN relay

Production-grade RFC 5766 TURN server with channel binding and TCP support. Stop paying for hosted TURN. ~30% of real-world WebRTC connections need relay through symmetric NATs; this handles them.

See src/turn/samples/turnserver/.

HTTP that outperforms Go

72,000 req/s with keep-alive on a single-core micro VM. Built on the same libuv + llhttp that powers Node.js, minus the runtime, GC, and language bridge.

Server Req/sec Latency
Raw libuv+llhttp 96,088 1.04ms
Icey 72,209 1.43ms
Go 1.25 net/http 53,878 2.31ms
Node.js v20 45,514 3.56ms

Icey delivers 75% of raw libuv throughput while providing a complete HTTP stack (connection management, header construction, WebSocket upgrade, streaming responses). It outperforms Go's net/http by 34% and Node.js by 59%. All three share the same foundation (libuv for async IO, llhttp for HTTP parsing); the difference is pure runtime overhead.

See src/http/samples/httpbenchmark/ for methodology.

Quick Start

Requirements

Platform Compiler
Linux GCC 12+ or Clang 15+
macOS AppleClang 15+ (Xcode 15+)
Windows MSVC 2022 (Visual Studio 17+)

CMake 3.21+ and pkg-config (Linux/macOS) required. Everything else is fetched automatically:

Dependency Version
libuv 1.50
llhttp 9.2.1
OpenSSL 3.x
nlohmann/json 3.11.3
zlib 1.3.1

Optional: FFmpeg 5+/6+/7+ (-DWITH_FFMPEG=ON), OpenCV 3.0+ (-DWITH_OPENCV=ON), libdatachannel (-DWITH_LIBDATACHANNEL=ON).

Build from source

git clone https://github.com/sourcey/icey.git
cd icey
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build --parallel $(nproc)
ctest --test-dir build --output-on-failure

CMake FetchContent

include(FetchContent)
FetchContent_Declare(icey
  GIT_REPOSITORY https://github.com/sourcey/icey.git
  GIT_TAG v2.1.0
)
FetchContent_MakeAvailable(icey)
target_link_libraries(myapp PRIVATE icy_base icy_net icy_http)

find_package

After installing (cmake --install build):

find_package(Icey REQUIRED)
target_link_libraries(myapp PRIVATE icy_base icy_net icy_http)

Code Examples

Media pipeline

Camera to encoder to network:

PacketStream stream;
stream.attachSource(videoCapture);
stream.attach(new av::MultiplexPacketEncoder(opts), 5);
stream.attach(socket, 10);
stream.start();

HTTP server

http::Server srv{ "127.0.0.1", 1337 };
srv.Connection += [](http::ServerConnection::Ptr conn) {
    conn->Payload += [](http::ServerConnection& conn, const MutableBuffer& buffer) {
        conn.send(bufferCast<const char*>(buffer), buffer.size());
        conn.close();
    };
};
srv.start();

WebRTC peer session

wrtc::PeerSession::Config config;
config.rtcConfig.iceServers.emplace_back("stun:stun.l.google.com:19302");
config.mediaOpts.videoCodec = av::VideoCodec("H264", "libx264", 1280, 720, 30);

wrtc::SympleSignaller signaller(client);
wrtc::PeerSession session(signaller, config);

session.IncomingCall += [&](const std::string& peerId) {
    session.accept();
};

session.StateChanged += [&](wrtc::PeerSession::State state) {
    if (state == wrtc::PeerSession::State::Active)
        startStreaming(session);
};

Modules

14 modules. Include only what you need; dependencies resolve automatically.

Module What it does
base Event loop (libuv), signals, streams, logging, filesystem, timers
crypto Hashing, HMAC, RSA, X509 (OpenSSL 3.x)
net TCP, SSL/TLS, UDP sockets, DNS
http HTTP server/client, WebSocket, cookies, streaming, keep-alive
json JSON serialisation (nlohmann/json)
av FFmpeg capture, encode, decode, record, stream (FFmpeg 5/6/7)
symple Real-time messaging, presence, rooms, WebRTC call signalling
stun RFC 5389 STUN for NAT traversal
turn RFC 5766 TURN relay server
webrtc WebRTC via libdatachannel: media bridge, peer sessions, codec negotiation
archo ZIP/archive handling
pluga Plugin system (shared library loading)
pacm Package manager for plugin distribution
sched Task scheduler for deferred/periodic jobs

Contributors

  • Kam Low (@auscaster) - Creator and primary developer
  • Sergey Parfenyuk (@sparfenyuk) - macOS compile fixes, type corrections, buffer handling
  • Yury Shubin (@yuryshubin) - iOS build toolchain and platform fixes
  • Norm Ovenseri (@normano) - Apple/FFmpeg builds, AVFoundation support, verbose logging
  • Igor Lutsyk (@lutsykigor) - WebRTC/WebSocket fixes, OpenCV+WebRTC sample app, Firefox compatibility
  • Kryton (@Malesio) - Segfault fixes and Valgrind cleanup
  • Vinci Xu (@VinciShark) - Windows documentation, testing and updates
  • Michael Fig (@michael-fig) - Compiler flags for building without FFmpeg
  • Stanislav Kapulkin (@kapulkin) - WebRTC modernisation and macOS compile definitions
  • Thomas Reichhart (@blackforest-tom) - FFmpeg constant updates and ARM build fixes
  • Artem Suprunov (@artemiuzzz) - WebRTC null pointer fix and library path resolution
  • Hyunuk Kim (@surinkim) - Windows std::codecvt unicode conversion fix
  • Cameron Smith (@cksmith) - Git line ending normalisation
  • Damian Zelim (@ZelimDamian) - OS X compiler flag fixes
  • Alexey (@deilos) - Cross-platform FFmpeg build script fixes

Contributing

PRs welcome. See the contributing guide for code style, tests, and workflow.

About

C++14 evented IO libraries for high performance networking and media based applications

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors