All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- System-dependency builds no longer vendor and install
nlohmann/json.hpp, avoiding conflicts with downstream package managers that already providenlohmann-json - Exported CMake package configs now propagate the
nlohmann_jsondependency when system deps are enabled
- Realtime intelligence pipeline primitives in the
visionmodule, including frame normalization for detector-ready frames - WebRTC receive jitter buffer for smoother inbound media playback
- API reference docs for graft, speech, and vision modules
- Markdown sanitization script for generated API docs
- Permanent macOS system-deps + FFmpeg CI coverage matching downstream package-manager builds
- Doxygen config updated for new module coverage
- Docs now use latest sourcey toolchain
- Module guide updated with graft, speech, vision entries
- pluga module and docs (replaced by graft)
- Apple AVFoundation source compatibility for package-manager builds on modern macOS SDKs
- Darwin shared-library symbols are preserved for downstream package-manager builds
- Downstream package-manager builds now resolve
libuvcorrectly when the system dependency is exported as a shared target instead of a static-only alias - RPM source-package staging now carries the downstream compatibility patch set and claims the installed umbrella and vendored JSON headers in the development package
- MacPorts builds now force the supported
ReleaseCMake build type instead of inheriting the unsupported default from the port group - The GitHub release workflow now validates the pre-finalize tag state, so tagged releases can publish before
make release-finalizepins the archive hashes onmain
- Package-manager builds now accept the shared
libuv::uvtarget exported byvcpkgand other system package layouts - Windows consumers no longer rely on backported export/linkage fixes for
Timeout,PacketFactory,Transaction, and the baseuvwrapper templates - FFmpeg-backed builds now propagate discovered library search directories so Windows link steps can resolve
avcodec.liband related FFmpeg libraries
- Release tooling now treats Conan like the rest of the package-manager surfaces:
make releaseseedspackaging/conan/conandata.ymlmake release-finalizepins the tagged archive hash and runs the final consistency check
- The documented release flow is now explicit about the post-tag finalize step instead of suggesting a pre-tag
release-checkthat could only fail on placeholder hashes
- Alpine system-package builds now accept the shared
llhttptargets exported by Alpine'sllhttp-devpackage instead of hard-linkingllhttp_static - The local Conan recipe now builds from immutable tagged GitHub archives instead of exporting a live checkout into the package
- Repo-local package manager layouts under
packaging/withmake package-conanandmake package-vcpkgentry points for local consumer validation - Homebrew tap formulae, Debian / PPA source-package scaffolding, and release hooks for those package surfaces
- Browser smoke coverage for the
src/webrtc/apps/media-server/web/frontend and media-server interop path - Reportable microbenchmark runner plus focused parse/probe benches for packet stream, HTTP, WebSocket, and STUN hot paths
- Focused
webrtcbenchandsymplebenchtargets covering:- WebRTC sender dispatch
- WebRTC receiver dispatch
- Symple room fanout
- Symple client parse/dispatch
- API overview quality checks in the docs toolchain to catch shallow or missing generated reference summaries
- Internal Symple protocol/state helpers for welcome parsing, sanitized presence emission, room reconciliation, and roster presence application, backed by focused unit coverage
webrtc_supportas a dedicated support library for Symple/WebSocket signallers, including the server-side virtual-peer adapter now used by samples andmedia-server
- WebRTC browser/media-server behavior after 2.3.0: codec negotiation is tighter, browser offer handling is stricter, and
PeerSession/ track flow is more defensive under real browser traffic - Public package, docs, and sample surface now use lowercase
iceyconsistently, with no retainedIceyCMake/package-manager naming - Public lifecycle verbs are now standardized on
start()/stop()across HTTP client/server, Symple client/server, and TURN client surfaces; the previous spellings were removed instead of being kept as compatibility aliases - WebRTC internals are now cut along explicit seams instead of monolithic translation units:
PeerSessionstate/signalling/RTC work is split acrosspeersession.cpp,peersessionsignalling.cpp, andpeersessionrtc.cpp- remote answer scoping lives in
remotemediaplan.cpp - codec support is centralized in
codecregistry.cpp media-serverruntime code is split underapps/media-server/internal/
- Public Symple and TURN configuration handoff now favors value-style option snapshots instead of live mutable option bags
- Public option mutation is now explicit on package-manager surfaces via
mutableOptions(), while the public AV encoder interfaces expose const option views PeerSession::Confignow exposes media settings underconfig.media, and answer-session media options are derived explicitly from the remote offer's mids, directions, and payload types instead of relying on looser implicit defaults- Symple client/server internals are now organized under
src/symple/src/client/andsrc/symple/src/server/, with shared wire helpers insrc/symple/src/protocol.* - API reference coverage has been deepened across the core modules, WebRTC, and the pacm/pluga surfaces, with reorganized Sourcey guides and workflows
- icey docs now consume published
moxygen/sourceynpm releases instead of git-pinned or exact-pinned toolchain versions - Packaging assets are centralized under
packaging/instead of spreading Conan and vcpkg files across the repo root - Installed
icey.pcmetadata now uses the current package-manager-facing description instead of the old networking-only tagline - Browser support claims are now explicit:
- Chromium and Firefox are validated by the committed Playwright smoke
- Playwright WebKit on Linux is treated as non-authoritative for Safari/WebRTC publish-path claims
- Exported CMake package consumers now tolerate dependency target-name differences across FetchContent, Conan, and vcpkg installs
- Repo-local Conan and vcpkg packaging validation now passes sequentially, and installed
icey.hheaders no longer leak absolute build/source paths - Browser media-server smoke harness failures and related WebRTC sample pipeline regressions that were blocking stricter interop coverage
- Symple-backed WebRTC signalling now enforces full
user|idpeer identity on the public call boundary instead of accepting ambiguous bare user/session forms - WebRTC receive-side codec detection no longer stringifies track descriptions back into SDP just to rediscover codec/clock data on bind or record paths
- Symple clients now transition cleanly to
Erroron underlying transport failures, including initial WebSocket connect failures, via explicithttp::ClientConnectionerror propagation - pacm JSON handling in the submodule update path, keeping the external package-manager surface aligned with the current docs/API snapshots
- Dedicated protocol fuzz targets under
BUILD_FUZZERSfor the HTTP parser, WebSocket frame parser, STUN message parser, and TURN indication/request parsing - Build-tree and install-tree consumer validation for the exported CMake package surface
- Dedicated microbenchmark targets under
BUILD_BENCHMARKS:signalbench,httpbench,httpparsebench, andwsbench, plus the separateBUILD_PERFhttpperf/httpperf_comparewrk/Node.js/Go comparison harness - WebRTC loopback/media regression coverage for:
- data-channel roundtrip
- encoded H.264 roundtrip
- encoded Opus roundtrip
- relay audio fanout
- relay source handoff
media-serverrecordandrelaymodes are now real shipped behaviors rather than placeholder sample code
- HTTP benchmark assets moved out of
src/http/samples/httpbenchmark/intosrc/http/perf/, whilesrc/http/bench/now stays microbench-only andsrc/http/samples/contains samples only PacketStreamqueue retention and ownership semantics are now explicit at queue boundaries, with deterministic overload/drop accounting and synchronized multi-source passthroughPeerSessionnow uses explicit call phases and stricter signaling ordering, and WebRTC track setup requires explicit codec selection instead of fallback codec guessing- The canonical WebRTC send path used by the samples and
media-serveris now encode -> RTP packetize -> sender, with stable sender bindings across call lifecycles
- Core async lifetime handling in
base/net/http, including retained handle/request context, state-driven HTTP server lifecycle, and typed WebSocket parse errors - STUN/TURN protocol correctness and relay hot paths:
- correct STUN length/integrity patching
- correct plain vs XOR address handling
- IPv6 address attribute wire sizing
- binary/numeric TURN permission checks and local-IP policy
- Symple routing/state correctness:
- presence canonicalization
- room-based authz checks
- reconnect room restoration
- virtual-peer copy semantics
media-serverrelay/signaling behavior:- remote ICE candidates queue until remote SDP is installed
- relay source promotion/handoff works correctly
- stable media sender lifetime across attach/detach
- Package consumer discovery:
- exported targets work correctly from both the build tree and install tree
find_package(icey REQUIRED COMPONENTS ...)correctly marks built components as found
- Symple v4 server hardening: max connections, per-peer rate limiting (token bucket), max message size, graceful shutdown broadcast with error responses (413/429/503)
- Team/group permission scoping: rooms as permission boundaries,
roomsfield in auth message,Authenticatesignal passes mutableroomsvector for server-side assignment - Direct message permission check: sender and recipient must share at least one room
- Protocol version in welcome message (
"protocol": "symple/4") and room list in welcome response - ASAN CMake support:
-fsanitize=addresspropagates to FetchContent C dependencies viaCMAKE_C_FLAGS http::Serveroverloads acceptingnet::TCPSocket::Ptrfor TLS/SSL support alongsideuv::Loop*constructorssmpl::Serveracceptsuv::Loop*parameter, threads loop through tohttp::Server- Timer destructor calls
stop(), timer callback null-checksreq->data(defense-in-depth) Peer::operator=with explicitstatic_cast<const json::Value&>(avoids nlohmann template overload)- Symple module README with server, client, WebRTC signalling, and permissions documentation
- 4 hardening tests: max connections, max message size, rate limiting, graceful shutdown
- Mutex deadlock in
Responder::onPayload:connection().close()for close/auth-reject was called inside the lock; moved toshouldCloseflag pattern outside lock scope - Use-after-free in
Server::onConnectionClose: non-pooled connections were destroyed synchronously during their own callback chain; now deferred via idle callback (found by ASAN) expect()macro silently skipped side effects inNDEBUGmode:waitFor()calls insideexpect()were never executed in Release builds
http::Serverprimary constructor takesuv::Loop*instead ofnet::TCPSocket::Ptr; loop is stored as_loopmember, used in shutdown and deferred destruction- SympleSignaller and all WebRTC samples updated from
smpl::TCPClient/SSLClienttosmpl::Client
socketiomodule deleted entirely (replaced by Symple v4 native WebSocket protocol)- All
socketioreferences removed from CMakeLists.txt across symple, webrtc samples
- WebRTC module with libdatachannel integration (no Google libwebrtc dependency)
- Three-layer architecture: track factories (layer 1), per-track PacketStream adapters (layer 2), MediaBridge + PeerSession convenience wrappers (layer 3)
- Transport-agnostic
SignallingInterfaceabstract base class; PeerSession no longer depends on Symple SympleSignaller: Symple call protocol implementation (call:init/accept/reject/offer/answer/candidate/hangup)- Four WebRTC sample applications: data-echo, webcam-streamer, file-streamer, media-recorder
CodecNegotiatormaps RTP codecs to FFmpeg encoders at runtime- CI job for WebRTC builds (Ubuntu 24.04, gcc-14, FFmpeg + OpenSSL + libdatachannel)
- Symple v4 protocol: native WebSocket transport replacing Socket.IO (breaking change from v3)
- C++ Symple server: auth, rooms, routing, presence, shutdown lifecycle
- C++ Symple client: plain WebSocket, auth flow, reconnection, close guard
- Protocol spec (
PROTOCOL.md): connection lifecycle, message types, addressing, rooms, auth modes, scaling, v3-v4 migration - 14 integration tests (address, peer, message, presence, roster, server lifecycle, connect, two-peer presence, message routing, auth failure, disconnect presence)
test::waitFor()helper for event-driven async test patterns
ConnectionPool: LIFO stack reuses ServerConnection objects across requests (configurable max size, default 128)ServerConnection::reset(): swap sockets without reconstruction (zero-alloc steady state)DateCache: formatted Date header updated once per second via timer- HTTP/1.1 keep-alive: persistent connections, parser resets via
on_message_begin, idle timeout (default 30s) IntrusivePtr<T>andRefCounted<T>: intrusive smart pointer with non-atomic embedded refcount (zero allocation overhead vs shared_ptr)- Go net/http server added to benchmark suite for comparison
- Pre-formatted static status lines for common HTTP codes
USE_SYSTEM_DEPSCMake option for vcpkg/system package integration (switches FetchContent to find_package for libuv, llhttp, zlib, minizip)- Vendored nlohmann/json single header (v3.11.3), eliminating FetchContent download
- vcpkg port with full dependency manifest and feature flags
- Symplestreamer sample: camera/file video streaming to symple-player via MJPEG over WebSocket
- HTTP benchmark suite and performance section in README
SO_REUSEPORTsupport viaServer::setReusePort()for multicore HTTP servingNullSharedMutexandLocalSignalfor lock-free signal dispatch on single-threaded event loopsICY_DATA_DIRcompile definition for test/sample data paths
Handle::Contextchanged fromshared_ptrtounique_ptr(sole ownership, eliminates control block allocation per handle)Handle::Context::~Contextclearshandle->data = nullptrbeforeuv_closeto prevent use-after-free in pending callbacksStream::writecallback null-checkshandle->databefore accessing the Stream objecthttp::Server::shutdown()defers connection destruction viauv_idlecallback (prevents write callback use-after-free)smpl::Server::shutdown()uses_shuttingDownatomic flag to guard Responder callbacks during destructionsmpl::Client::close()uses_closingguard to prevent re-entrant close- Symple client no longer depends on socketio module; uses native WebSocket via HTTP module
- WebRTC module
DEPENDSdrops symple (signalling is transport-agnostic) SKIP_EXPORToption added toicy_add_modulefor FetchContent dependency conflicts- README rewritten: WebRTC hero example, comparison table vs libWebRTC/libdatachannel/GStreamer/Pion, architecture diagram, module table
- Conan recipe added with per-module components and optional FFmpeg/OpenCV features
std::endlreplaced with'\n'throughout (removes redundant flush from logger macros)std::bindreplaced with lambdas throughout- Iterator loops converted to range-based for with structured bindings
typedefconverted tousingaliasesoverrideadded to virtual methods in derived classesstring_view: converted ~270 read-onlyconst std::string¶meters across base, http, crypto, net, symple, av, json, stun, turn modulesrandom: rewrote PRNG withstd::mt19937+std::random_device, replacing BSD nonlinear additive feedback LFSRdatetime: rewrote internals with C++20std::chronocalendar types (year_month_day,sys_days,weekday), replacing Julian Day floating-point mathnumeric: rewrote sprintf-based number formatting withstd::to_chars, consolidated 50 functions into shared templatesutil: replaced 10icomparetemplate overloads (~140 lines) with singlestring_viewimplementation- Renamed
json::valuetojson::Valuefor codebase consistency - HTTP server connections container changed from
vectortounordered_mapfor O(1) lookup - Single-pass response header assembly with pre-computed reserve
- All net/http signals switched to
LocalSignal, eliminating mutex acquisition per emit uv_write_trequest pooling via freelist in Stream (avoids heap alloc per write)- Static status code string lookup in
Response::write - Cached peer address after connect (avoids repeated
uv_tcp_getpeernamecalls) - Modernised all sample applications
- Moved test data (
test.mp4) to top-leveldata/directory shared across modules - Refreshed README with contributors section, moved build/contributing docs to
docs/
sendHeader()undefined behavior: response buffer moved from local to member (localstd::stringwas freed before asyncuv_writecompleted)UV_EOFinStream::handleRead()now triggers graceful close instead of error state- Symple v4 client
Peerto json assignment neededstatic_cast<const json::Value&>to avoid nlohmanntype_error.302 -Werrorenabled in CI; fixed all remaining compiler warningsjson::findNestedObjectWithPropertywas comparingit.value()instead ofit.key()Configuration::removeAlliterator invalidationLogStreamstub destructor forENABLE_LOGGING=OFFbuildshttpclientsampleStatusCodeenum streaming
- WebRTC hard dependency on Symple (signalling is now pluggable via
SignallingInterface) - Stale
bench/directory (duplicate ofsrc/http/samples/httpbenchmark) - Dead commented-out JsonCpp stringify code in json module
- Empty
json.cppstub - Deprecated
thread.h/thread.cppandarchotest file - In-source build artifacts
Keep-alive benchmark results (single vCPU, wrk -t4 -c100 -d10s):
| Server | req/s | 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 |
- macOS build: added missing
<unistd.h>and<cstdio>includes in platform.cpp - Windows build: added missing
<ostream>include in error.h (MSVC string_view needs full definition) - Windows build: replaced
SIGKILLwithSIGTERMin process.h (SIGKILL not defined on MSVC) - Windows build: added
<csignal>to application.cpp and process.h - macOS linker: removed unnecessary
-ldlfrom Apple config in icey.cmake - CI: fixed sanitizer env var syntax (split matrix.env into env_name/env_value)
- CI: removed ccache from coverage job (incompatible with gcov instrumentation)
- CI: added
submodules: recursiveto all checkout steps - CI: added
BUILD_APPLICATIONS=OFFto all jobs - pacm: fixed apps/CMakeLists.txt referencing
pacmconsoleinstead ofpacm-cli - Windows build: added missing
<windows.h>include in filesystem.cpp - Windows build: fixed winsock.h/winsock2.h include order conflict in x509certificate.cpp
- Windows build: added zlib include path for minizip in archo module
- Windows CI: fixed ctest syntax (
--config->-C Release)
-
Removed 7 dead
#if 0code blocks (packetstream.cpp, cipher.cpp, turn tests, deprecated thread.h) -
Cleaned up 11 stale TODO/FIXME comments in samples and deprecated code
-
Replaced all
assert()calls in production code with exceptions or early returns (40+ occurrences) -
Removed all
_WIN32_WCE(Windows CE) compatibility code -
Standardised Windows platform guard from
WIN32to_WIN32 -
Fixed
conston value return types in buffer.h (12 compiler warnings) -
Added
[[fallthrough]]annotations to base64.cpp switch cases (5 warnings) -
Added
overrideto Thread::start() and AsyncLogWriter::run() -
Fixed Thread template constructor init order warning (two-phase init)
-
Zero compiler warnings in project code (excluding third-party deps)
- Cross-platform process test (spawn echo, capture stdout, verify exit code)
- AV test data integrity verification: codec, sample rate, channel count, and duration checks via avformat
- SSL hostname verification integration test (connects to google.com with certificate verification enabled)
- Rewrote CMake build system with modern target-based dependencies and
icy_add_module()pattern - Replaced vendored sources with CMake FetchContent (libuv, zlib, llhttp, nlohmann/json)
- CMake 3.21 minimum, with proper
find_package(icey)and FetchContent consumer support - CPack packaging support (deb, rpm, tar.gz)
- External module plugin pattern for out-of-tree modules
- Multi-stage Docker build on Ubuntu 24.04 LTS
- GitHub Actions CI replacing CircleCI (Linux GCC/Clang, macOS AppleClang, Windows MSVC)
- CI includes address/undefined sanitizers and code coverage
.clang-formatand.clang-tidyconfigurations added
- C++20 minimum standard (from C++14)
#pragma oncereplaces include guards throughoutusingtype aliases replacetypedefnullptrreplacesNULLenum classreplaces unscoped enums- Smart pointers (unique_ptr, shared_ptr) throughout; RAII wrappers for OpenSSL, FFmpeg, and logger resources
std::chronoreplacesclock_ttiming[[nodiscard]]on key return values (crypto, net, av, archo modules)noexcepton all destructorsstd::invoke_resultreplaces deprecatedstd::result_of- C-style casts replaced with
static_cast/reinterpret_cast - Entire codebase formatted with clang-format
- OpenSSL 3.x migration (new provider/context APIs, deprecated function replacements)
- FFmpeg 6.x/7.x migration (avcodec send/receive API, codecpar, channel layouts,
av_*iterators) - llhttp replaces http_parser for HTTP/1.x parsing
- nlohmann/json replaces JsonCpp
- libuv 1.50 compatibility (accessor APIs, proper handle close, loop alive checks)
- FindOpenCV.cmake rewritten for OpenCV 4.x
- pacm: Default checksum algorithm changed from MD5 to SHA256
- pacm: SSL certificate verification enabled (was
initNoVerifyClient) - pacm: Path traversal protection added to zip extraction and package metadata
- pacm: Default API endpoint changed from http to https
- net: SSL hostname verification via
SSL_set1_host(), auto-wired throughSSLSocket::connect(host, port) - net: TLS 1.2 enforced as minimum protocol version
- net: ALPN protocol negotiation support (
SSLContext::setALPNProtocols) - net: SNI support (
SSLContext::enableSNI)
- STUN: ChannelNumber attribute factory was creating wrong type (ICEUseCandidate instead)
- STUN: Bit shift undefined behavior in UInt32/UInt64Attribute::setBit
- TURN: Permission lifetime corrected to RFC 5766-mandated 5 minutes (was 3)
- TURN: Server::stop() null pointer dereference when sockets not configured
- archo: ZipFile::closeCurrentFile() was calling unzOpenCurrentFile instead of unzCloseCurrentFile
- archo: Compressed/uncompressed file size fields were swapped
- base: once.h missing uv_close() on prepare handle (resource leak)
- base: UV_EOF in Stream::handleRead() now triggers graceful close instead of error state (fixes shutdown() causing spurious errors)
- base: IPC waitForSync() now has configurable timeout (default 5s) preventing infinite hangs
- base: Signal slot ID uniqueness enforced, throws on collision
- base: DateTime constructor/assign validate all components at runtime (was assert-only)
- base: PacketStream destructor warns instead of asserting on non-terminal state
- base: TaskRunner::add() fixed stray semicolon bug that allowed duplicate task insertion
- crypto: EVP_md5 replaced with EVP_sha256 for cipher key derivation
- av: MediaCapture::run() fixed stray semicolon on looping seek (was a no-op)
- av: MultiplexEncoder PTS tracking now mutex-protected across audio/video paths
- av: MediaCapture flags (_stopping, _looping, etc.) changed to std::atomic for thread safety
- av: FFmpeg 6.x const AVOutputFormat compatibility (removed oformat->codec assignment)
- av: Unchecked FFmpeg return values now checked: avformat_write_header, av_write_trailer, avio_close, avcodec_parameters_from_context
- http: Download progress tracking re-enabled via ClientConnection::IncomingProgress signal
- http: WebSocket RFC 6455 compliance: RSV bit validation, opcode validation, mask enforcement, ping/pong auto-response, close frame handshake
- http: WebSocket partial frame buffering across TCP segments
- http: WebSocket fragmentation/continuation frame support
- net: SSLContext min proto version was set before null check on context (crash fix)
- net: SSLAdapter SSL_ERROR_WANT_WRITE now calls flushWriteBIO instead of asserting
- pacm: latestSDKAsset() logic simplified (dead code branch removed)
- sched: Scheduler::update() and run() memory leaks fixed (erased tasks without deleting)
- All
assert()calls in production source code replaced with runtime checks (throws, returns, or warnings) - approximately 110+ asserts across 30 files in base, net, av, crypto, pacm modules - All stale
#if 0dead code blocks removed across 13+ source files - Stream::write() backpressure via configurable high water mark on libuv write queue (default 16MB)
- TaskRunner ownership modernised:
std::deque<std::unique_ptr<Task>>replaces raw pointer container
- util module merged into base (headers moved, includes updated, downstream deps fixed)
- archo module updated to use
std::filesystem::path - Socket.IO protocol v5 compatibility verified
- FindFFmpeg.cmake rewritten to use pkg-config (old version depended on deleted CMake macros)
- pacm and pluga modules fully modernised (noexcept destructors, dead code removed, typos fixed)
- base: Timer pause/resume, timer one-shot, IPC round-trip, logger level filtering, PacketStream overflow
- av: Standalone VideoEncoder (synthetic YUV -> libx264), VideoDecoder (test.mp4), AudioDecoder (test.mp4)
- http: Request/response serialisation round-trip, POST request, WebSocket text/binary frame encoding, client masking, control frames
- pacm: Package JSON round-trip, asset version selection, LocalPackage state management, manifest operations, error handling, InstallationState strings
- WebRTC module (unmaintainable due to Google API churn)
- Deprecated code:
av/deprecated/,UserManager,SharedObject - Dead connection classes from http/client.h
- CircleCI configuration
- Gitbook documentation tooling
- Vendored libuv, zlib, http-parser sources
- Legacy Poco and Anionu dependencies
- Last release of the 1.x series