A brief comparison of libdatachannel and libWebRTC
Note: This comparison was conducted mid-2024.
WebRTC (Web Real-Time Communication) is a standard for data, audio and video streaming. It is used in the browser without the need for plugins and allows features like video and voice chat to function in real time. Applications that run in the browser can use the WebRTC Javascript API that is standardized by the World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF) which ensures cross-browser support.
C++ applications can use Google’s WebRTC implementation which I’ll refer to as libWebRTC, which is used within Google Chrome. Although it is large and complex library, it does offer a comprehensive set of features.
libdatachannel is an alternative open source C++ WebRTC implementation. It is significantly smaller than libWebRTC, lacks many WebRTC features, but it is far simpler to use than libWebRTC.
Ease of building and Integration
Building libdatachannel is relatively straightforward. It uses the CMake build system, and developers can build by cloning the repository, running git submodule update --init
and executing the CMake command.
This is in contrast to libWebRTC which uses Google’s GN build system. GN manages dependencies and configurations but can introduce challenges when integrating into other projects. In fact, just syncing libWebRTC takes longer than the whole build process of libdatachannel.
libdatachannel has minimal dependencies including libJuice, a tasty open source project by the same developer for UDP NAT traversal. For certificate signing, the library can switch between OpenSSL (used by default), GnuTLS or Mbed TLS. libWebRTC depends on hundreds of libraries. Because of the sheer amount of dependencies, this can cause issues such as symbol collisions when integrating into other applications.
At time of writing, the compiled libdatachannel library sits only at 20MB rather than 600MB for libWebRTC for a Windows Release build.
libdatachannel provides some accessible examples which are easy to run and demonstrate how to stream video and audio. They provide Python scripts for setting up a signaling and web server to demonstrate streaming. libWebRTC also provides some examples, although there have been bug reports of some issues.
Code Differences
libdatachannel prefers to use std::function
callbacks rather than libWebRTC’s use of interfaces and inheritance.
For instance, libWebRTC uses virtual methods that it expects users to implement, such as in the PeerConnectionObserver
example below:
class PeerConnectionObserver {
public:
// Snippet from PeerConnectionObserver
virtual void OnSignalingChange(PeerConnectionInterface::SignalingState new_state) = 0;
virtual void OnDataChannel(rtc::scoped_refptr<DataChannelInterface> data_channel) = 0;
virtual void OnIceConnectionChange(PeerConnectionInterface::IceConnectionState new_state) {}
virtual void OnStandardizedIceConnectionChange(PeerConnectionInterface::IceConnectionState new_state) {}
virtual void OnConnectionChange(PeerConnectionInterface::PeerConnectionState new_state) {}
virtual void OnIceGatheringChange( PeerConnectionInterface::IceGatheringState new_state) = 0;
virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0;
};
libdatachannel by contrast, uses std::function
for callbacks, as shown in PeerConnection
below:
class PeerConnection final : CheshireCat<impl::PeerConnection> {
public:
// Snippet from PeerConnection
void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
void onLocalDescription(std::function<void(Description description)> callback);
void onLocalCandidate(std::function<void(Candidate candidate)> callback);
void onStateChange(std::function<void(State state)> callback);
void onIceStateChange(std::function<void(IceState state)> callback);
void onGatheringStateChange(std::function<void(GatheringState state)> callback);
void onSignalingStateChange(std::function<void(SignalingState state)> callback);
};
libdatachannel aims to maintain a stable ABI by utilizing the PIMPL idiom (also known as the CheshireCat technique). This design pattern involves keeping implementation details in a separate, private class, allowing the interface to remain unchanged even as the underlying implementation changes.
Packetization and Codec support
Both libraries provide packetizers, which take video data and split these into multiple packets. They also provide de-packetizers to re-assemble these packets on the receiving side. This is needed because an internet packet only has a limited size, and video frames can be larger than this packet size. Furthermore, each codec has a different standard as to how they are turned into packets.
libdatachannel supports packetization for H.264 (AVC), H.265 (HEVC) and AV1. However it is missing VP8 and VP9 support. When it comes to de-packization, it only supports H.264. libdatachannel does not provide any encoders or decoders, so relies on developers to bring their own.
libWebRTC in comparison supports H.264, VP8, VP9, AV1 and is adding support for H.265 for packetization and de-packetization. This support is complemented with software encoders and decoders for all of those mentioned, while also allowing users to provide their own.
Bandwidth estimation and bitrate
The feature I found missing the most in libdatachannel was bandwidth estimation and receiving video bitrate requests. For instance, libWebRTC includes sophisticated estimation of bandwidth, which is crucial for maintaining optimal video quality and minimizing buffering or disruptions during bad network conditions. libWebRTC will call SetRates
to supply a bitrate that it expects the video to be encoded at in order to keep within the estimated bandwidth.
libdatachannel does not manage bandwidth estimation or bitrate adjustments. The developer just calls send
and does not receive bitrate requests. This simpler approach is adequate for controlled network conditions or when supplying a single pre-encoded video or data.
Conclusion
The simplicity of libdatachannel stands out as its greatest strength. For developers who just need to stream video, audio or plain data, the library’s straightforward use makes it appealing. As per the library’s name, it is perfect for streaming data when bandwidth is not a concern. Its small nature makes it easy to integrate into projects and the library also acts as a good base to add features onto.
The library is missing many crucial features like bandwidth estimation, codec encoding and decoding support, so projects needing that may be better opting for Google’s libWebRTC implementation.
However, libdatachannel is receiving regular updates and features are being added. I look forward to seeing the progression and evolution of the library.
Links
- GnuTLS https://www.gnutls.org/
- Google’s WebRTC implementation libWebRTC https://webrtc.googlesource.com/src
- libdatachannel https://github.com/paullouisageneau/libdatachannel
- libJuice https://github.com/paullouisageneau/libjuice
- Mbed TLS https://github.com/Mbed-TLS/mbedtls
- OpenSSL https://openssl-library.org/
- PIMPL idiom https://en.cppreference.com/w/cpp/language/pimpl