diff --git a/libdatachannel/LICENSE b/libdatachannel/LICENSE new file mode 100644 index 0000000..3d76a56 --- /dev/null +++ b/libdatachannel/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 RandomityGuy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libdatachannel/include/rtc/av1rtppacketizer.hpp b/libdatachannel/include/rtc/av1rtppacketizer.hpp new file mode 100644 index 0000000..c2b2b79 --- /dev/null +++ b/libdatachannel/include/rtc/av1rtppacketizer.hpp @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2023 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_AV1_RTP_PACKETIZER_H +#define RTC_AV1_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "nalunit.hpp" +#include "rtppacketizer.hpp" + +namespace rtc { + +// RTP packetization of AV1 payload +class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer { +public: + inline static const uint32_t ClockRate = VideoClockRate; + [[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate; + + // Define how OBUs are seperated in a AV1 Sample + enum class Packetization { + Obu = RTC_OBU_PACKETIZED_OBU, + TemporalUnit = RTC_OBU_PACKETIZED_TEMPORAL_UNIT, + }; + + // Constructs AV1 payload packetizer with given RTP configuration. + // @note RTP configuration is used in packetization process which may change some configuration + // properties such as sequence number. + AV1RtpPacketizer(Packetization packetization, shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + +private: + static std::vector extractTemporalUnitObus(const binary &data); + + std::vector fragment(binary data) override; + std::vector fragmentObu(const binary &data); + + const Packetization mPacketization; + const size_t mMaxFragmentSize; + + std::unique_ptr mSequenceHeader; +}; + +// For backward compatibility, do not use +using AV1PacketizationHandler [[deprecated("Add AV1RtpPacketizer directly")]] = PacketizationHandler; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_AV1_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/candidate.hpp b/libdatachannel/include/rtc/candidate.hpp new file mode 100644 index 0000000..00ca20d --- /dev/null +++ b/libdatachannel/include/rtc/candidate.hpp @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_CANDIDATE_H +#define RTC_CANDIDATE_H + +#include "common.hpp" + +#include + +namespace rtc { + +class RTC_CPP_EXPORT Candidate { +public: + enum class Family { Unresolved, Ipv4, Ipv6 }; + enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed }; + enum class TransportType { Unknown, Udp, TcpActive, TcpPassive, TcpSo, TcpUnknown }; + + Candidate(); + Candidate(string candidate); + Candidate(string candidate, string mid); + + void hintMid(string mid); + void changeAddress(string addr); + void changeAddress(string addr, uint16_t port); + void changeAddress(string addr, string service); + + enum class ResolveMode { Simple, Lookup }; + bool resolve(ResolveMode mode = ResolveMode::Simple); + + Type type() const; + TransportType transportType() const; + uint32_t priority() const; + string candidate() const; + string mid() const; + operator string() const; + + bool operator==(const Candidate &other) const; + bool operator!=(const Candidate &other) const; + + bool isResolved() const; + Family family() const; + optional address() const; + optional port() const; + +private: + void parse(string candidate); + + string mFoundation; + uint32_t mComponent, mPriority; + string mTypeString, mTransportString; + Type mType; + TransportType mTransportType; + string mNode, mService; + string mTail; + + optional mMid; + + // Extracted on resolution + Family mFamily; + string mAddress; + uint16_t mPort; +}; + +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Candidate &candidate); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Candidate::Type &type); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, + const Candidate::TransportType &transportType); + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/channel.hpp b/libdatachannel/include/rtc/channel.hpp new file mode 100644 index 0000000..384279d --- /dev/null +++ b/libdatachannel/include/rtc/channel.hpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2019-2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_CHANNEL_H +#define RTC_CHANNEL_H + +#include "common.hpp" + +#include +#include + +namespace rtc { + +namespace impl { +struct Channel; +} + +class RTC_CPP_EXPORT Channel : private CheshireCat { +public: + virtual ~Channel(); + + virtual void close() = 0; + virtual bool send(message_variant data) = 0; // returns false if buffered + virtual bool send(const byte *data, size_t size) = 0; + + virtual bool isOpen() const = 0; + virtual bool isClosed() const = 0; + virtual size_t maxMessageSize() const; // max message size in a call to send + virtual size_t bufferedAmount() const; // total size buffered to send + + void onOpen(std::function callback); + void onClosed(std::function callback); + void onError(std::function callback); + + void onMessage(std::function callback); + void onMessage(std::function binaryCallback, + std::function stringCallback); + + void onBufferedAmountLow(std::function callback); + void setBufferedAmountLowThreshold(size_t amount); + + void resetCallbacks(); + + // Extended API + optional receive(); // only if onMessage unset + optional peek(); // only if onMessage unset + size_t availableAmount() const; // total size available to receive + void onAvailable(std::function callback); + +protected: + Channel(impl_ptr impl); +}; + +} // namespace rtc + +#endif // RTC_CHANNEL_H diff --git a/libdatachannel/include/rtc/common.hpp b/libdatachannel/include/rtc/common.hpp new file mode 100644 index 0000000..371f18f --- /dev/null +++ b/libdatachannel/include/rtc/common.hpp @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_COMMON_H +#define RTC_COMMON_H + +#ifdef RTC_STATIC +#define RTC_CPP_EXPORT +#else // dynamic library +#ifdef _WIN32 +#ifdef RTC_EXPORTS +#define RTC_CPP_EXPORT __declspec(dllexport) // building the library +#else +#define RTC_CPP_EXPORT __declspec(dllimport) // using the library +#endif +#else // not WIN32 +#define RTC_CPP_EXPORT +#endif +#endif + +#ifdef _WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 // Windows 8 +#endif +#ifdef _MSC_VER +#pragma warning(disable : 4251) // disable "X needs to have dll-interface..." +#endif +#endif + +#ifndef RTC_ENABLE_WEBSOCKET +#define RTC_ENABLE_WEBSOCKET 1 +#endif + +#ifndef RTC_ENABLE_MEDIA +#define RTC_ENABLE_MEDIA 1 +#endif + +#include "rtc.h" // for C API defines + +#include "utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rtc { + +using std::byte; +using std::nullopt; +using std::optional; +using std::shared_ptr; +using std::string; +using std::string_view; +using std::unique_ptr; +using std::variant; +using std::weak_ptr; + +using binary = std::vector; +using message_variant = variant; + +using std::int16_t; +using std::int32_t; +using std::int64_t; +using std::int8_t; +using std::ptrdiff_t; +using std::size_t; +using std::uint16_t; +using std::uint32_t; +using std::uint64_t; +using std::uint8_t; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/configuration.hpp b/libdatachannel/include/rtc/configuration.hpp new file mode 100644 index 0000000..d02fa7e --- /dev/null +++ b/libdatachannel/include/rtc/configuration.hpp @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_ICE_CONFIGURATION_H +#define RTC_ICE_CONFIGURATION_H + +#include "common.hpp" + +#include + +namespace rtc { + +struct RTC_CPP_EXPORT IceServer { + enum class Type { Stun, Turn }; + enum class RelayType { TurnUdp, TurnTcp, TurnTls }; + + // Any type + IceServer(const string &url); + + // STUN + IceServer(string hostname_, uint16_t port_); + IceServer(string hostname_, string service_); + + // TURN + IceServer(string hostname_, uint16_t port, string username_, string password_, + RelayType relayType_ = RelayType::TurnUdp); + IceServer(string hostname_, string service_, string username_, string password_, + RelayType relayType_ = RelayType::TurnUdp); + + string hostname; + uint16_t port; + Type type; + string username; + string password; + RelayType relayType; +}; + +struct RTC_CPP_EXPORT ProxyServer { + enum class Type { Http, Socks5 }; + + ProxyServer(const string &url); + + ProxyServer(Type type_, string hostname_, uint16_t port_); + ProxyServer(Type type_, string hostname_, uint16_t port_, string username_, string password_); + + Type type; + string hostname; + uint16_t port; + optional username; + optional password; +}; + +enum class CertificateType { + Default = RTC_CERTIFICATE_DEFAULT, // ECDSA + Ecdsa = RTC_CERTIFICATE_ECDSA, + Rsa = RTC_CERTIFICATE_RSA +}; + +enum class TransportPolicy { All = RTC_TRANSPORT_POLICY_ALL, Relay = RTC_TRANSPORT_POLICY_RELAY }; + +struct RTC_CPP_EXPORT Configuration { + // ICE settings + std::vector iceServers; + optional proxyServer; // libnice only + optional bindAddress; // libjuice only, default any + + // Options + CertificateType certificateType = CertificateType::Default; + TransportPolicy iceTransportPolicy = TransportPolicy::All; + bool enableIceTcp = false; + bool enableIceUdpMux = false; // libjuice only + bool disableAutoNegotiation = false; + bool disableAutoGathering = false; + bool forceMediaTransport = false; + bool disableFingerprintVerification = false; + + // Port range + uint16_t portRangeBegin = 1024; + uint16_t portRangeEnd = 65535; + + // Network MTU + optional mtu; + + // Local maximum message size for Data Channels + optional maxMessageSize; + + // Certificates and private keys + optional certificatePemFile; + optional keyPemFile; + optional keyPemPass; +}; + +#ifdef RTC_ENABLE_WEBSOCKET + +struct WebSocketConfiguration { + bool disableTlsVerification = false; // if true, don't verify the TLS certificate + optional proxyServer; // only non-authenticated http supported for now + std::vector protocols; + optional tcpConnectionTimeout; // zero to disable + optional connectionTimeout; // zero to disable + optional pingInterval; // zero to disable + optional maxOutstandingPings; + optional caCertificatePemFile; + optional certificatePemFile; + optional keyPemFile; + optional keyPemPass; + optional maxMessageSize; +}; + +struct WebSocketServerConfiguration { + uint16_t port = 8080; + bool enableTls = false; + optional certificatePemFile; + optional keyPemFile; + optional keyPemPass; + optional bindAddress; + optional connectionTimeout; + optional maxMessageSize; +}; + +#endif + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/datachannel.hpp b/libdatachannel/include/rtc/datachannel.hpp new file mode 100644 index 0000000..0e83a97 --- /dev/null +++ b/libdatachannel/include/rtc/datachannel.hpp @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_DATA_CHANNEL_H +#define RTC_DATA_CHANNEL_H + +#include "channel.hpp" +#include "common.hpp" +#include "reliability.hpp" + +#include + +namespace rtc { + +namespace impl { + +struct DataChannel; +struct PeerConnection; + +} // namespace impl + +class RTC_CPP_EXPORT DataChannel final : private CheshireCat, public Channel { +public: + DataChannel(impl_ptr impl); + ~DataChannel() override; + + optional stream() const; + optional id() const; + string label() const; + string protocol() const; + Reliability reliability() const; + + bool isOpen(void) const override; + bool isClosed(void) const override; + size_t maxMessageSize() const override; + + void close(void) override; + bool send(message_variant data) override; + bool send(const byte *data, size_t size) override; + template bool sendBuffer(const Buffer &buf); + template bool sendBuffer(Iterator first, Iterator last); + +private: + using CheshireCat::impl; +}; + +template std::pair to_bytes(const Buffer &buf) { + using T = typename std::remove_pointer::type; + using E = typename std::conditional::value, byte, T>::type; + return std::make_pair(static_cast(static_cast(buf.data())), + buf.size() * sizeof(E)); +} + +template bool DataChannel::sendBuffer(const Buffer &buf) { + auto [bytes, size] = to_bytes(buf); + return send(bytes, size); +} + +template bool DataChannel::sendBuffer(Iterator first, Iterator last) { + size_t size = 0; + for (Iterator it = first; it != last; ++it) + size += it->size(); + + binary buffer(size); + byte *pos = buffer.data(); + for (Iterator it = first; it != last; ++it) { + auto [bytes, len] = to_bytes(*it); + pos = std::copy(bytes, bytes + len, pos); + } + return send(std::move(buffer)); +} + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/dependencydescriptor.hpp b/libdatachannel/include/rtc/dependencydescriptor.hpp new file mode 100644 index 0000000..59ddcdf --- /dev/null +++ b/libdatachannel/include/rtc/dependencydescriptor.hpp @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2024 Shigemasa Watanabe (Wandbox) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_DEPENDENCY_DESCRIPTOR_H +#define RTC_DEPENDENCY_DESCRIPTOR_H + +#include "common.hpp" + +#include + +namespace rtc { + +struct BitWriter { + static BitWriter fromSizeBits(byte *buf, size_t offsetBits, size_t sizeBits); + static BitWriter fromNull(); + + size_t getWrittenBits() const; + + bool write(uint64_t v, size_t bits); + // Write non-symmetric unsigned encoded integer + // ref: https://aomediacodec.github.io/av1-rtp-spec/#a82-syntax + bool writeNonSymmetric(uint64_t v, uint64_t n); + +private: + size_t writePartialByte(uint8_t *p, size_t offset, uint64_t v, size_t bits); + +private: + byte *mBuf = nullptr; + size_t mInitialOffset = 0; + size_t mOffset = 0; + size_t mSize = 0; +}; + +enum class DecodeTargetIndication { + NotPresent = 0, + Discardable = 1, + Switch = 2, + Required = 3, +}; + +struct RenderResolution { + int width = 0; + int height = 0; +}; + +struct FrameDependencyTemplate { + int spatialId = 0; + int temporalId = 0; + std::vector decodeTargetIndications; + std::vector frameDiffs; + std::vector chainDiffs; +}; + +struct FrameDependencyStructure { + int templateIdOffset = 0; + int decodeTargetCount = 0; + int chainCount = 0; + std::vector decodeTargetProtectedBy; + std::vector resolutions; + std::vector templates; +}; + +struct DependencyDescriptor { + bool startOfFrame = true; + bool endOfFrame = true; + int frameNumber = 0; + FrameDependencyTemplate dependencyTemplate; + std::optional resolution; + std::optional activeDecodeTargetsBitmask; + bool structureAttached; +}; + +struct DependencyDescriptorContext { + DependencyDescriptor descriptor; + std::bitset<32> activeChains; + FrameDependencyStructure structure; +}; + +// Write dependency descriptor to RTP Header Extension +// Dependency descriptor specification is here: +// https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension +class DependencyDescriptorWriter { +public: + explicit DependencyDescriptorWriter(const DependencyDescriptorContext& context); + size_t getSizeBits() const; + size_t getSize() const; + void writeTo(byte *buf, size_t sizeBytes) const; + +private: + void doWriteTo(BitWriter &writer) const; + void writeBits(BitWriter &writer, uint64_t v, size_t bits) const; + void writeNonSymmetric(BitWriter &writer, uint64_t v, uint64_t n) const; + +private: + const FrameDependencyStructure &mStructure; + std::bitset<32> mActiveChains; + const DependencyDescriptor &mDescriptor; +}; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/description.hpp b/libdatachannel/include/rtc/description.hpp new file mode 100644 index 0000000..573190d --- /dev/null +++ b/libdatachannel/include/rtc/description.hpp @@ -0,0 +1,401 @@ +/** + * Copyright (c) 2019-2020 Paul-Louis Ageneau + * Copyright (c) 2020 Staz Modrzynski + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_DESCRIPTION_H +#define RTC_DESCRIPTION_H + +#include "candidate.hpp" +#include "common.hpp" +#include "rtp.hpp" + +#include +#include +#include + +namespace rtc { + +const string DEFAULT_OPUS_AUDIO_PROFILE = + "minptime=10;maxaveragebitrate=96000;stereo=1;sprop-stereo=1;useinbandfec=1"; + +// Use Constrained Baseline profile Level 3.1 (necessary for Firefox) +// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs +// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this. +const string DEFAULT_H264_VIDEO_PROFILE = + "profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1"; + +struct CertificateFingerprint { + enum class Algorithm { Sha1, Sha224, Sha256, Sha384, Sha512 }; + static string AlgorithmIdentifier(Algorithm algorithm); + static size_t AlgorithmSize(Algorithm algorithm); + + bool isValid() const; + + Algorithm algorithm; + string value; +}; + +class RTC_CPP_EXPORT Description { +public: + enum class Type { Unspec, Offer, Answer, Pranswer, Rollback }; + enum class Role { ActPass, Passive, Active }; + + enum class Direction { + SendOnly = RTC_DIRECTION_SENDONLY, + RecvOnly = RTC_DIRECTION_RECVONLY, + SendRecv = RTC_DIRECTION_SENDRECV, + Inactive = RTC_DIRECTION_INACTIVE, + Unknown = RTC_DIRECTION_UNKNOWN + }; + + Description(const string &sdp, Type type = Type::Unspec, Role role = Role::ActPass); + Description(const string &sdp, string typeString); + + Type type() const; + string typeString() const; + Role role() const; + string bundleMid() const; + std::vector iceOptions() const; + optional iceUfrag() const; + optional icePwd() const; + optional fingerprint() const; + bool ended() const; + + void hintType(Type type); + void addIceOption(string option); + void removeIceOption(const string &option); + void setIceAttribute(string ufrag, string pwd); + void setFingerprint(CertificateFingerprint f); + + std::vector attributes() const; + void addAttribute(string attr); + void removeAttribute(const string &attr); + + std::vector candidates() const; + std::vector extractCandidates(); + bool hasCandidate(const Candidate &candidate) const; + void addCandidate(Candidate candidate); + void addCandidates(std::vector candidates); + void endCandidates(); + + operator string() const; + string generateSdp(string_view eol = "\r\n") const; + string generateApplicationSdp(string_view eol = "\r\n") const; + + class RTC_CPP_EXPORT RidAttribute { + public: + RidAttribute(string name, string value); + + [[nodiscard]] const string &name() const; + [[nodiscard]] const string &value() const; + + void value(string value); + + private: + string mName; + string mValue; + }; + + class RTC_CPP_EXPORT Rid { + public: + explicit Rid(string rid); + Rid(string rid, std::vector attributes); + + [[nodiscard]] const string &rid() const; + [[nodiscard]] const std::vector &attributes() const; + + private: + string mRid; + std::vector mAttributes; + }; + + class RTC_CPP_EXPORT RidBuilder { + public: + explicit RidBuilder(string rid); + + // https://datatracker.ietf.org/doc/html/rfc8851#name-arid-restrictions + RidBuilder& max_width(uint32_t value); + RidBuilder& max_height(uint32_t value); + RidBuilder& max_fps(uint32_t value); + RidBuilder& max_br(uint32_t value); + + RidBuilder& custom(string key, string value); + + // One time use only + [[nodiscard]] Rid build(); + + private: + string mRid; + std::vector mAttributes; + + RidBuilder& saveAttribute(string key, string value); + }; + + class RTC_CPP_EXPORT Entry { + public: + virtual ~Entry() = default; + + virtual string type() const; + virtual string protocol() const; + virtual string description() const; + virtual string mid() const; + + Direction direction() const; + void setDirection(Direction dir); + + bool isRemoved() const; + void markRemoved(); + + std::vector attributes() const; + void addAttribute(string attr); + void removeAttribute(const string &attr); + void addRid(string rid); // Just the name + void addRid(Rid rid); // With RFC 8851 attributes + + std::vector rids() const; + + struct RTC_CPP_EXPORT ExtMap { + static int parseId(string_view description); + + ExtMap(int id, string uri, Direction direction = Direction::Unknown); + ExtMap(string_view description); + + void setDescription(string_view description); + + int id; + string uri; + string attributes; + Direction direction = Direction::Unknown; + }; + + std::vector extIds(); + ExtMap *extMap(int id); + const ExtMap *extMap(int id) const; + void addExtMap(ExtMap map); + void removeExtMap(int id); + + operator string() const; + string generateSdp(string_view eol = "\r\n", string_view addr = "0.0.0.0", + uint16_t port = 9) const; + + virtual void parseSdpLine(string_view line); + + protected: + Entry(const string &mline, string mid, Direction dir = Direction::Unknown); + + virtual string generateSdpLines(string_view eol) const; + + std::vector mAttributes; + std::map mExtMaps; + + private: + string mType; + string mProtocol; + string mDescription; + string mMid; + std::vector mRids; + Direction mDirection; + bool mIsRemoved; + }; + + struct RTC_CPP_EXPORT Application : public Entry { + public: + Application(string mid = "data"); + Application(const string &mline, string mid); + virtual ~Application() = default; + + Application reciprocate() const; + + void setSctpPort(uint16_t port); + void hintSctpPort(uint16_t port); + void setMaxMessageSize(size_t size); + + optional sctpPort() const; + optional maxMessageSize() const; + + virtual void parseSdpLine(string_view line) override; + + private: + virtual string generateSdpLines(string_view eol) const override; + + optional mSctpPort; + optional mMaxMessageSize; + }; + + // Media (non-data) + class RTC_CPP_EXPORT Media : public Entry { + public: + Media(const string &mline, string mid, Direction dir = Direction::SendOnly); + Media(const string &sdp); + virtual ~Media() = default; + + string description() const override; + Media reciprocate() const; + + void addSSRC(uint32_t ssrc, optional name, optional msid = nullopt, + optional trackId = nullopt); + void removeSSRC(uint32_t ssrc); + void replaceSSRC(uint32_t old, uint32_t ssrc, optional name, + optional msid = nullopt, optional trackID = nullopt); + bool hasSSRC(uint32_t ssrc) const; + void clearSSRCs(); + std::vector getSSRCs() const; + optional getCNameForSsrc(uint32_t ssrc) const; + + void addRtxSSRC(SSRC primarySsrc, SSRC rtxSsrc, optional cname = nullopt); + void removeRtxSSRC(SSRC primarySsrc); + optional getRtxSsrcForSsrc(SSRC primarySsrc) const; + optional getSsrcForRtxSsrc(SSRC rtxSsrc) const; + + int bitrate() const; + void setBitrate(int bitrate); + + struct RTC_CPP_EXPORT RtpMap { + static int parsePayloadType(string_view description); + + explicit RtpMap(int payloadType); + RtpMap(string_view description); + + void setDescription(string_view description); + + void addFeedback(string fb); + void removeFeedback(const string &str); + bool hasFeedback(const string &str) const; + + void addParameter(string p); + void removeParameter(const string &str); + + int payloadType; + string format; + int clockRate; + string encParams; + + std::vector rtcpFbs; + std::vector fmtps; + }; + + bool hasPayloadType(int payloadType) const; + std::vector payloadTypes() const; + RtpMap *rtpMap(int payloadType); + const RtpMap *rtpMap(int payloadType) const; + void addRtpMap(RtpMap map); + void removeRtpMap(int payloadType); + void removeFormat(const string &format); + + void addRtxCodec(int payloadType, int origPayloadType, unsigned int clockRate); + + optional getRtxPayloadType(int primaryPayloadType) const; + + bool isRtxEnabled() const; + void disableRtx(); + + virtual void parseSdpLine(string_view line) override; + + private: + virtual string generateSdpLines(string_view eol) const override; + + int mBas = -1; + + std::vector mOrderedPayloadTypes; + std::map mRtpMaps; + std::vector mSsrcs; + std::map mCNameMap; + std::map mSsrcToRtxSsrc; // primary_ssrc -> rtx_ssrc + }; + + class RTC_CPP_EXPORT Audio : public Media { + public: + Audio(string mid = "audio", Direction dir = Direction::SendOnly); + + void addAudioCodec(int payloadType, string codec, optional profile = std::nullopt); + void addOpusCodec(int payloadType, optional profile = DEFAULT_OPUS_AUDIO_PROFILE); + void addPCMACodec(int payloadType, optional profile = std::nullopt); + void addPCMUCodec(int payloadType, optional profile = std::nullopt); + void addAACCodec(int payloadType, optional profile = std::nullopt); + void addG722Codec(int payloadType, optional profile = std::nullopt); + + [[deprecated("Use addAACCodec")]] inline void + addAacCodec(int payloadType, optional profile = std::nullopt) { + addAACCodec(payloadType, std::move(profile)); + }; + }; + + class RTC_CPP_EXPORT Video : public Media { + public: + Video(string mid = "video", Direction dir = Direction::SendOnly); + + void addVideoCodec(int payloadType, string codec, optional profile = std::nullopt); + + void addH264Codec(int payloadType, optional profile = DEFAULT_H264_VIDEO_PROFILE); + void addH265Codec(int payloadType, optional profile = std::nullopt); + void addVP8Codec(int payloadType, optional profile = std::nullopt); + void addVP9Codec(int payloadType, optional profile = std::nullopt); + void addAV1Codec(int payloadType, optional profile = std::nullopt); + }; + + // Adds RTX payload type mappings for SDP negotiation (codec level only). + // For per-stream RTX, also call Media::addRtxSSRC() once SSRCs are known. + // Set audio=true to also apply RTX to audio media streams. + void addRtx(optional clockRate = nullopt, bool audio = false); + + bool hasApplication() const; + bool hasAudioOrVideo() const; + bool hasMid(string_view mid) const; + + int addMedia(Media media); + int addMedia(Application application); + int addApplication(string mid = "data"); + int addVideo(string mid = "video", Direction dir = Direction::SendOnly); + int addAudio(string mid = "audio", Direction dir = Direction::SendOnly); + void clearMedia(); + + variant media(int index); + variant media(int index) const; + int mediaCount() const; + + const Application *application() const; + Application *application(); + + static Type stringToType(const string &typeString); + static string typeToString(Type type); + + string sessionId() const; + +private: + optional defaultCandidate() const; + shared_ptr createEntry(string mline, string mid, Direction dir); + void removeApplication(); + + Type mType; + + // Session-level attributes + Role mRole; + string mUsername; + string mSessionId; + std::vector mIceOptions; + optional mIceUfrag, mIcePwd; + optional mFingerprint; + std::vector mAttributes; // other attributes + + // Entries + std::vector> mEntries; + shared_ptr mApplication; + + // Candidates + std::vector mCandidates; + bool mEnded = false; +}; + +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Description &description); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, Description::Type type); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, Description::Role role); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Description::Direction &direction); + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/frameinfo.hpp b/libdatachannel/include/rtc/frameinfo.hpp new file mode 100644 index 0000000..2747e9f --- /dev/null +++ b/libdatachannel/include/rtc/frameinfo.hpp @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019-2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_FRAMEINFO_H +#define RTC_FRAMEINFO_H + +#include "common.hpp" + +#include + +namespace rtc { + +struct RTC_CPP_EXPORT FrameInfo { + FrameInfo(uint32_t timestamp) : timestamp(timestamp) {}; + template> FrameInfo(std::chrono::duration timestamp) : timestampSeconds(timestamp) {} + + [[deprecated]] FrameInfo(uint8_t payloadType, uint32_t timestamp) : timestamp(timestamp), payloadType(payloadType) {}; + + uint32_t timestamp = 0; + uint8_t payloadType = 0; + + optional> timestampSeconds; + + bool isKeyFrame = false; // Set by the application + + /// Absolute capture timestamp in NTP-format (high 32 bits seconds since + /// the NTP epoch 1900-01-01 UTC; low 32 bits fractional seconds). When + /// set alongside an RtpPacketizationConfig with absCaptureTimeId > 0, + /// the RtpPacketizer writes the 8-byte form of the abs-capture-time RTP + /// header extension on every packet of this frame. Receivers use this + /// to recover the source's capture wallclock and compute glass-to-glass + /// latency. + /// https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time + optional absCaptureTimeNtp; +}; + +} // namespace rtc + +#endif // RTC_FRAMEINFO_H diff --git a/libdatachannel/include/rtc/global.hpp b/libdatachannel/include/rtc/global.hpp new file mode 100644 index 0000000..47b6133 --- /dev/null +++ b/libdatachannel/include/rtc/global.hpp @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2020-2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_GLOBAL_H +#define RTC_GLOBAL_H + +#include "common.hpp" + +#include +#include +#include + +namespace rtc { + +#ifdef None +#undef None +#endif + +enum class LogLevel { // Don't change, it must match plog severity + None = 0, + Fatal = 1, + Error = 2, + Warning = 3, + Info = 4, + Debug = 5, + Verbose = 6 +}; + +typedef std::function LogCallback; + +RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr); + +RTC_CPP_EXPORT void SetThreadPoolSize(unsigned int count); // 0: hardware concurrency + +struct SctpSettings { + // For the following settings, not set means optimized default + optional recvBufferSize; // in bytes + optional sendBufferSize; // in bytes + optional maxChunksOnQueue; // in chunks + optional initialCongestionWindow; // in MTUs + optional maxBurst; // in MTUs + optional congestionControlModule; // 0: RFC2581, 1: HSTCP, 2: H-TCP, 3: RTCC + optional delayedSackTime; + optional minRetransmitTimeout; + optional maxRetransmitTimeout; + optional initialRetransmitTimeout; + optional maxRetransmitAttempts; + optional heartbeatInterval; +}; + +RTC_CPP_EXPORT void SetSctpSettings(SctpSettings s); + +// Optional global preload and cleanup +RTC_CPP_EXPORT bool Preload(); +RTC_CPP_EXPORT std::shared_future Cleanup(); + +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level); + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/h264rtpdepacketizer.hpp b/libdatachannel/include/rtc/h264rtpdepacketizer.hpp new file mode 100644 index 0000000..19bbe97 --- /dev/null +++ b/libdatachannel/include/rtc/h264rtpdepacketizer.hpp @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_H264_RTP_DEPACKETIZER_H +#define RTC_H264_RTP_DEPACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "message.hpp" +#include "nalunit.hpp" +#include "rtp.hpp" +#include "rtpdepacketizer.hpp" + +namespace rtc { + +/// RTP depacketization for H264 +class RTC_CPP_EXPORT H264RtpDepacketizer final : public VideoRtpDepacketizer { +public: + using Separator = NalUnit::Separator; + + H264RtpDepacketizer(Separator separator = Separator::StartSequence); + ~H264RtpDepacketizer(); + +private: + message_ptr reassemble(message_buffer &buffer) override; + void addSeparator(binary &frame); + + const NalUnit::Separator mSeparator; +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif /* RTC_H264_RTP_DEPACKETIZER_H */ diff --git a/libdatachannel/include/rtc/h264rtppacketizer.hpp b/libdatachannel/include/rtc/h264rtppacketizer.hpp new file mode 100644 index 0000000..ffc5f2e --- /dev/null +++ b/libdatachannel/include/rtc/h264rtppacketizer.hpp @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * Copyright (c) 2023 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_H264_RTP_PACKETIZER_H +#define RTC_H264_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "nalunit.hpp" +#include "rtppacketizer.hpp" + +namespace rtc { + +/// RTP packetization for H264 +class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer { +public: + using Separator = NalUnit::Separator; + + inline static const uint32_t ClockRate = VideoClockRate; + [[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate; + + /// Constructs h264 payload packetizer with given RTP configuration. + /// @note RTP configuration is used in packetization process which may change some configuration + /// properties such as sequence number. + /// @param separator NAL unit separator + /// @param rtpConfig RTP configuration + /// @param maxFragmentSize maximum size of one NALU fragment + H264RtpPacketizer(Separator separator, shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + + // For backward compatibility, do not use + [[deprecated]] H264RtpPacketizer( + shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + +private: + std::vector fragment(binary data) override; + std::vector splitFrame(const binary &frame); + + const Separator mSeparator; + const size_t mMaxFragmentSize; +}; + +// For backward compatibility, do not use +using H264PacketizationHandler [[deprecated("Add H264RtpPacketizer directly")]] = PacketizationHandler; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_H264_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/h265nalunit.hpp b/libdatachannel/include/rtc/h265nalunit.hpp new file mode 100644 index 0000000..ad40cf0 --- /dev/null +++ b/libdatachannel/include/rtc/h265nalunit.hpp @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2023 Zita Liao (Dolby) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_H265_NAL_UNIT_H +#define RTC_H265_NAL_UNIT_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "nalunit.hpp" + +#include +#include + +namespace rtc { + +#pragma pack(push, 1) + +#define H265_FU_HEADER_SIZE 1 +/// Nalu header +struct RTC_CPP_EXPORT H265NalUnitHeader { + /* + * nal_unit_header( ) { + * forbidden_zero_bit f(1) + * nal_unit_type u(6) + * nuh_layer_id u(6) + * nuh_temporal_id_plus1 u(3) + } + */ + uint8_t _first = 0; // high byte of header + uint8_t _second = 0; // low byte of header + + bool forbiddenBit() const { return _first >> 7; } + uint8_t unitType() const { return (_first & 0b0111'1110) >> 1; } + uint8_t nuhLayerId() const { return ((_first & 0x1) << 5) | ((_second & 0b1111'1000) >> 3); } + uint8_t nuhTempIdPlus1() const { return _second & 0b111; } + + void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); } + void setUnitType(uint8_t type) { _first = (_first & 0b1000'0001) | ((type & 0b11'1111) << 1); } + void setNuhLayerId(uint8_t nuhLayerId) { + _first = (_first & 0b1111'1110) | ((nuhLayerId & 0b10'0000) >> 5); + _second = (_second & 0b0000'0111) | ((nuhLayerId & 0b01'1111) << 3); + } + void setNuhTempIdPlus1(uint8_t nuhTempIdPlus1) { + _second = (_second & 0b1111'1000) | (nuhTempIdPlus1 & 0b111); + } +}; + +/// Nalu fragment header +struct RTC_CPP_EXPORT H265NalUnitFragmentHeader { + /* + * +---------------+ + * |0|1|2|3|4|5|6|7| + * +-+-+-+-+-+-+-+-+ + * |S|E| FuType | + * +---------------+ + */ + uint8_t _first = 0; + + bool isStart() const { return _first >> 7; } + bool isEnd() const { return (_first >> 6) & 0x01; } + uint8_t unitType() const { return _first & 0b11'1111; } + + void setStart(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); } + void setEnd(bool isSet) { _first = (_first & 0b1011'1111) | (isSet << 6); } + void setUnitType(uint8_t type) { _first = (_first & 0b1100'0000) | (type & 0b11'1111); } +}; + +#pragma pack(pop) + +struct H265NalUnitFragment; + +/// NAL unit +struct RTC_CPP_EXPORT H265NalUnit : NalUnit { + static std::vector GenerateFragments(const std::vector &nalus, + size_t maxFragmentSize); + + H265NalUnit(const H265NalUnit &unit) = default; + H265NalUnit(size_t size, bool includingHeader = true) + : NalUnit(size, includingHeader, NalUnit::Type::H265) {} + H265NalUnit(binary &&data) : NalUnit(std::move(data)) {} + H265NalUnit() : NalUnit(NalUnit::Type::H265) {} + + template + H265NalUnit(Iterator begin_, Iterator end_) : NalUnit(begin_, end_) {} + + bool forbiddenBit() const { return header()->forbiddenBit(); } + uint8_t unitType() const { return header()->unitType(); } + uint8_t nuhLayerId() const { return header()->nuhLayerId(); } + uint8_t nuhTempIdPlus1() const { return header()->nuhTempIdPlus1(); } + + binary payload() const { + assert(size() >= H265_NAL_HEADER_SIZE); + return {begin() + H265_NAL_HEADER_SIZE, end()}; + } + + void setForbiddenBit(bool isSet) { header()->setForbiddenBit(isSet); } + void setUnitType(uint8_t type) { header()->setUnitType(type); } + void setNuhLayerId(uint8_t nuhLayerId) { header()->setNuhLayerId(nuhLayerId); } + void setNuhTempIdPlus1(uint8_t nuhTempIdPlus1) { header()->setNuhTempIdPlus1(nuhTempIdPlus1); } + + void setPayload(binary payload) { + assert(size() >= H265_NAL_HEADER_SIZE); + erase(begin() + H265_NAL_HEADER_SIZE, end()); + insert(end(), payload.begin(), payload.end()); + } + + std::vector generateFragments(size_t maxFragmentSize) const; + +protected: + const H265NalUnitHeader *header() const { + assert(size() >= H265_NAL_HEADER_SIZE); + return reinterpret_cast(data()); + } + + H265NalUnitHeader *header() { + assert(size() >= H265_NAL_HEADER_SIZE); + return reinterpret_cast(data()); + } +}; + +/// NAL unit fragment +struct RTC_CPP_EXPORT H265NalUnitFragment : H265NalUnit { + [[deprecated]] static std::vector> fragmentsFrom(shared_ptr nalu, + uint16_t maxFragmentSize); + + enum class FragmentType { Start, Middle, End }; + + H265NalUnitFragment(FragmentType type, bool forbiddenBit, uint8_t nuhLayerId, + uint8_t nuhTempIdPlus1, uint8_t unitType, binary data); + + uint8_t unitType() const { return fragmentHeader()->unitType(); } + + binary payload() const { + assert(size() >= H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE); + return {begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE, end()}; + } + + FragmentType type() const { + if (fragmentHeader()->isStart()) { + return FragmentType::Start; + } else if (fragmentHeader()->isEnd()) { + return FragmentType::End; + } else { + return FragmentType::Middle; + } + } + + void setUnitType(uint8_t type) { fragmentHeader()->setUnitType(type); } + + void setPayload(binary payload) { + assert(size() >= H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE); + erase(begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE, end()); + insert(end(), payload.begin(), payload.end()); + } + + void setFragmentType(FragmentType type); + +protected: + const uint8_t nal_type_fu = 49; + + H265NalUnitHeader *fragmentIndicator() { return reinterpret_cast(data()); } + + const H265NalUnitHeader *fragmentIndicator() const { + return reinterpret_cast(data()); + } + + H265NalUnitFragmentHeader *fragmentHeader() { + return reinterpret_cast(data() + H265_NAL_HEADER_SIZE); + } + + const H265NalUnitFragmentHeader *fragmentHeader() const { + return reinterpret_cast(data() + H265_NAL_HEADER_SIZE); + } +}; + +class [[deprecated]] RTC_CPP_EXPORT H265NalUnits : public std::vector> { +public: + static const uint16_t defaultMaximumFragmentSize = + uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6 + + std::vector> generateFragments(uint16_t maxFragmentSize); +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_NAL_UNIT_H */ diff --git a/libdatachannel/include/rtc/h265rtpdepacketizer.hpp b/libdatachannel/include/rtc/h265rtpdepacketizer.hpp new file mode 100644 index 0000000..0f48f02 --- /dev/null +++ b/libdatachannel/include/rtc/h265rtpdepacketizer.hpp @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020-2024 Paul-Louis Ageneau + * Copyright (c) 2024 Robert Edmonds + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_H265_RTP_DEPACKETIZER_H +#define RTC_H265_RTP_DEPACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "h265nalunit.hpp" +#include "message.hpp" +#include "rtp.hpp" +#include "rtpdepacketizer.hpp" + +#include + +namespace rtc { + +/// RTP depacketization for H265 +class RTC_CPP_EXPORT H265RtpDepacketizer final : public VideoRtpDepacketizer { +public: + using Separator = NalUnit::Separator; + + H265RtpDepacketizer(Separator separator = Separator::StartSequence); + ~H265RtpDepacketizer(); + +private: + message_ptr reassemble(message_buffer &buffer); + void addSeparator(binary &frame); + + const NalUnit::Separator mSeparator; +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_H265_RTP_DEPACKETIZER_H diff --git a/libdatachannel/include/rtc/h265rtppacketizer.hpp b/libdatachannel/include/rtc/h265rtppacketizer.hpp new file mode 100644 index 0000000..e788bac --- /dev/null +++ b/libdatachannel/include/rtc/h265rtppacketizer.hpp @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2023 Zita Liao (Dolby) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_H265_RTP_PACKETIZER_H +#define RTC_H265_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "h265nalunit.hpp" +#include "rtppacketizer.hpp" + +namespace rtc { + +// RTP packetization for H265 +class RTC_CPP_EXPORT H265RtpPacketizer final : public RtpPacketizer { +public: + using Separator = NalUnit::Separator; + + inline static const uint32_t ClockRate = VideoClockRate; + [[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate; + + // Constructs h265 payload packetizer with given RTP configuration. + // @note RTP configuration is used in packetization process which may change some configuration + // properties such as sequence number. + // @param separator NAL unit separator + // @param rtpConfig RTP configuration + // @param maxFragmentSize maximum size of one NALU fragment + H265RtpPacketizer(Separator separator, shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + + // For backward compatibility, do not use + [[deprecated]] H265RtpPacketizer(shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + +private: + std::vector fragment(binary data) override; + std::vector splitFrame(const binary &frame); + + const NalUnit::Separator mSeparator; + const size_t mMaxFragmentSize; +}; + +// For backward compatibility, do not use +using H265PacketizationHandler [[deprecated("Add H265RtpPacketizer directly")]] = PacketizationHandler; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_H265_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/iceudpmuxlistener.hpp b/libdatachannel/include/rtc/iceudpmuxlistener.hpp new file mode 100644 index 0000000..df72c4d --- /dev/null +++ b/libdatachannel/include/rtc/iceudpmuxlistener.hpp @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2025 Alex Potsides + * Copyright (c) 2025 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_ICE_UDP_MUX_LISTENER_H +#define RTC_ICE_UDP_MUX_LISTENER_H + +#include "common.hpp" + +namespace rtc { + +namespace impl { + +struct IceUdpMuxListener; + +} // namespace impl + +struct IceUdpMuxRequest { // TODO change name + string localUfrag; + string remoteUfrag; + string remoteAddress; + uint16_t remotePort; +}; + +class RTC_CPP_EXPORT IceUdpMuxListener final : private CheshireCat { +public: + IceUdpMuxListener(uint16_t port, optional bindAddress = nullopt); + ~IceUdpMuxListener(); + + void stop(); + + uint16_t port() const; + + void OnUnhandledStunRequest(std::function callback); + +private: + using CheshireCat::impl; +}; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/mediahandler.hpp b/libdatachannel/include/rtc/mediahandler.hpp new file mode 100644 index 0000000..32b9310 --- /dev/null +++ b/libdatachannel/include/rtc/mediahandler.hpp @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_MEDIA_HANDLER_H +#define RTC_MEDIA_HANDLER_H + +#include "common.hpp" +#include "description.hpp" +#include "message.hpp" + +#include + +namespace rtc { + +class RTC_CPP_EXPORT MediaHandler : public std::enable_shared_from_this { +public: + MediaHandler(); + virtual ~MediaHandler(); + + /// Called when a media is added or updated + /// @param desc Description of the media + virtual void media([[maybe_unused]] const Description::Media &desc) {} + + /// Called when there is traffic coming from the peer + /// @param messages Incoming messages from the peer, can be modified by the handler + /// @param send Send callback to send messages back to the peer + virtual void incoming([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {} + + /// Called when there is traffic that needs to be sent to the peer + /// @param messages Outgoing messages to the peer, can be modified by the handler + /// @param send Send callback to send messages back to the peer + virtual void outgoing([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {} + + virtual bool requestKeyframe(const std::vector& targetSSRCs, bool retransmit, const message_callback &send); + virtual bool requestBitrate(unsigned int bitrate, const message_callback &send); + + void addToChain(shared_ptr handler); + void setNext(shared_ptr handler); + shared_ptr next(); + shared_ptr next() const; + shared_ptr last(); // never null + shared_ptr last() const; // never null + + void mediaChain(const Description::Media &desc); + void incomingChain(message_vector &messages, const message_callback &send); + void outgoingChain(message_vector &messages, const message_callback &send); + +private: + shared_ptr mNext; +}; + +} // namespace rtc + +#endif // RTC_MEDIA_HANDLER_H diff --git a/libdatachannel/include/rtc/message.hpp b/libdatachannel/include/rtc/message.hpp new file mode 100644 index 0000000..52a7dbe --- /dev/null +++ b/libdatachannel/include/rtc/message.hpp @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2019-2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_MESSAGE_H +#define RTC_MESSAGE_H + +#include "common.hpp" +#include "frameinfo.hpp" +#include "reliability.hpp" + +#include + +namespace rtc { + +struct RTC_CPP_EXPORT Message : binary { + enum Type { Binary, String, Control, Reset }; + + Message(const Message &message) = default; + Message(size_t size, Type type_ = Binary) : binary(size), type(type_) {} + + template + Message(Iterator begin_, Iterator end_, Type type_ = Binary) + : binary(begin_, end_), type(type_) {} + + Message(binary &&data, Type type_ = Binary) : binary(std::move(data)), type(type_) {} + + Type type; + unsigned int stream = 0; // Stream id (SCTP stream or SSRC) + unsigned int dscp = 0; // Differentiated Services Code Point + shared_ptr reliability; + shared_ptr frameInfo; +}; + +using message_ptr = shared_ptr; +using message_callback = std::function; +using message_vector = std::vector; + +inline size_t message_size_func(const message_ptr &m) { + return m->type == Message::Binary || m->type == Message::String ? m->size() : 0; +} + +template +message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary, + unsigned int stream = 0, shared_ptr reliability = nullptr) { + auto message = std::make_shared(begin, end, type); + message->stream = stream; + message->reliability = reliability; + return message; +} + +template +message_ptr make_message(Iterator begin, Iterator end, shared_ptr frameInfo) { + auto message = std::make_shared(begin, end); + message->frameInfo = frameInfo; + return message; +} + +// For backward compatibiity, do not use +template +[[deprecated]] message_ptr make_message(Iterator begin, Iterator end, Message::Type type, + unsigned int stream, shared_ptr frameInfo) { + auto message = std::make_shared(begin, end, type); + message->stream = stream; + message->frameInfo = frameInfo; + return message; +} + +RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Message::Binary, + unsigned int stream = 0, + shared_ptr reliability = nullptr); + +RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary, + unsigned int stream = 0, + shared_ptr reliability = nullptr); + +RTC_CPP_EXPORT message_ptr make_message(binary &&data, shared_ptr frameInfo); + +RTC_CPP_EXPORT message_ptr make_message(size_t size, message_ptr orig); + +RTC_CPP_EXPORT message_ptr make_message(message_variant data); + +#if RTC_ENABLE_MEDIA + +// Reconstructs a message_ptr from an opaque rtcMessage pointer that +// was allocated by rtcCreateOpaqueMessage(). +message_ptr make_message_from_opaque_ptr(rtcMessage *&&message); + +#endif + +RTC_CPP_EXPORT message_variant to_variant(Message &&message); +RTC_CPP_EXPORT message_variant to_variant(const Message &message); + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/nalunit.hpp b/libdatachannel/include/rtc/nalunit.hpp new file mode 100644 index 0000000..ca8af8c --- /dev/null +++ b/libdatachannel/include/rtc/nalunit.hpp @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_NAL_UNIT_H +#define RTC_NAL_UNIT_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" + +#include +#include + +namespace rtc { + +#pragma pack(push, 1) + +/// Nalu header +struct RTC_CPP_EXPORT NalUnitHeader { + uint8_t _first = 0; + + bool forbiddenBit() const { return _first >> 7; } + uint8_t nri() const { return _first >> 5 & 0x03; } + uint8_t idc() const { return _first & 0x60; } + uint8_t unitType() const { return _first & 0x1F; } + + void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); } + void setNRI(uint8_t nri) { _first = (_first & 0x9F) | ((nri & 0x03) << 5); } + void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); } +}; + +/// Nalu fragment header +struct RTC_CPP_EXPORT NalUnitFragmentHeader { + uint8_t _first = 0; + + bool isStart() const { return _first >> 7; } + bool reservedBit6() const { return (_first >> 5) & 0x01; } + bool isEnd() const { return (_first >> 6) & 0x01; } + uint8_t unitType() const { return _first & 0x1F; } + + void setStart(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); } + void setEnd(bool isSet) { _first = (_first & 0xBF) | (isSet << 6); } + void setReservedBit6(bool isSet) { _first = (_first & 0xDF) | (isSet << 5); } + void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); } +}; + +#pragma pack(pop) + +enum NalUnitStartSequenceMatch { + NUSM_noMatch, + NUSM_firstZero, + NUSM_secondZero, + NUSM_thirdZero, + NUSM_shortMatch, + NUSM_longMatch +}; + +static const size_t H264_NAL_HEADER_SIZE = 1; +static const size_t H265_NAL_HEADER_SIZE = 2; + +struct NalUnitFragmentA; + +/// NAL unit +struct RTC_CPP_EXPORT NalUnit : binary { + static std::vector GenerateFragments(const std::vector &nalus, + size_t maxFragmentSize); + + enum class Separator { + Length = RTC_NAL_SEPARATOR_LENGTH, // first 4 bytes are NAL unit length + LongStartSequence = RTC_NAL_SEPARATOR_LONG_START_SEQUENCE, // 0x00, 0x00, 0x00, 0x01 + ShortStartSequence = RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE, // 0x00, 0x00, 0x01 + StartSequence = RTC_NAL_SEPARATOR_START_SEQUENCE, // LongStartSequence or ShortStartSequence + }; + + static NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match, + std::byte _byte, Separator separator); + + enum class Type { H264, H265 }; + + NalUnit(const NalUnit &unit) = default; + NalUnit(size_t size, bool includingHeader = true, Type type = Type::H264) + : binary(size + (includingHeader ? 0 + : (type == Type::H264 ? H264_NAL_HEADER_SIZE + : H265_NAL_HEADER_SIZE))) {} + NalUnit(binary &&data) : binary(std::move(data)) {} + NalUnit(Type type = Type::H264) + : binary(type == Type::H264 ? H264_NAL_HEADER_SIZE : H265_NAL_HEADER_SIZE) {} + template NalUnit(Iterator begin_, Iterator end_) : binary(begin_, end_) {} + + bool forbiddenBit() const { return header()->forbiddenBit(); } + uint8_t nri() const { return header()->nri(); } + uint8_t unitType() const { return header()->unitType(); } + + binary payload() const { + assert(size() >= 1); + return {begin() + 1, end()}; + } + + void setForbiddenBit(bool isSet) { header()->setForbiddenBit(isSet); } + void setNRI(uint8_t nri) { header()->setNRI(nri); } + void setUnitType(uint8_t type) { header()->setUnitType(type); } + + void setPayload(binary payload) { + assert(size() >= 1); + erase(begin() + 1, end()); + insert(end(), payload.begin(), payload.end()); + } + + std::vector generateFragments(size_t maxFragmentSize) const; + +protected: + const NalUnitHeader *header() const { + assert(size() >= 1); + return reinterpret_cast(data()); + } + + NalUnitHeader *header() { + assert(size() >= 1); + return reinterpret_cast(data()); + } +}; + +/// Nal unit fragment A +struct RTC_CPP_EXPORT NalUnitFragmentA : NalUnit { + // For backward compatibility, do not use + [[deprecated]] static std::vector> + fragmentsFrom(shared_ptr nalu, uint16_t maxFragmentSize); + + enum class FragmentType { Start, Middle, End }; + + NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType, + binary data); + + uint8_t unitType() const { return fragmentHeader()->unitType(); } + + binary payload() const { + assert(size() >= 2); + return {begin() + 2, end()}; + } + + FragmentType type() const { + if (fragmentHeader()->isStart()) { + return FragmentType::Start; + } else if (fragmentHeader()->isEnd()) { + return FragmentType::End; + } else { + return FragmentType::Middle; + } + } + + void setUnitType(uint8_t type) { fragmentHeader()->setUnitType(type); } + + void setPayload(binary payload) { + assert(size() >= 2); + erase(begin() + 2, end()); + insert(end(), payload.begin(), payload.end()); + } + + void setFragmentType(FragmentType type); + +protected: + const uint8_t nal_type_fu_A = 28; + + NalUnitHeader *fragmentIndicator() { return reinterpret_cast(data()); } + + const NalUnitHeader *fragmentIndicator() const { + return reinterpret_cast(data()); + } + + NalUnitFragmentHeader *fragmentHeader() { + return reinterpret_cast(fragmentIndicator() + 1); + } + + const NalUnitFragmentHeader *fragmentHeader() const { + return reinterpret_cast(fragmentIndicator() + 1); + } +}; + +// For backward compatibility, do not use +class [[deprecated]] RTC_CPP_EXPORT NalUnits : public std::vector> { +public: + static const uint16_t defaultMaximumFragmentSize = + uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6 + + std::vector> generateFragments(uint16_t maxFragmentSize); +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_NAL_UNIT_H */ diff --git a/libdatachannel/include/rtc/pacinghandler.hpp b/libdatachannel/include/rtc/pacinghandler.hpp new file mode 100644 index 0000000..60313d7 --- /dev/null +++ b/libdatachannel/include/rtc/pacinghandler.hpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2024 Sean DuBois + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_PACING_HANDLER_H +#define RTC_PACING_HANDLER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "utils.hpp" + +#include +#include + +namespace rtc { + +// Paced sending of RTP packets. It takes a stream of RTP packets that can have an uneven bitrate +// and delivers them in a smoother manner by sending a fixed size of them on an interval +class RTC_CPP_EXPORT PacingHandler : public MediaHandler { +public: + PacingHandler(double bitsPerSecond, std::chrono::milliseconds sendInterval, size_t maxQueueSize=0); + + void setBitrate(double bitsPerSecond); + + void setMaxQueueAmount(size_t maxQueueAmount); + + void onOverflow(std::function callback); + + void outgoing(message_vector &messages, const message_callback &send) override; + +private: + std::atomic mHaveScheduled = false; + + double mBytesPerSecond; + double mBudget; + + std::chrono::milliseconds mSendInterval; + std::chrono::time_point mLastRun; + + size_t mMaxQueueAmount; + + std::mutex mParamsMutex; + std::mutex mMutex; + std::queue mRtpBuffer; + + synchronized_callback<> mOverflowCallback; + + void schedule(const message_callback &send, std::chrono::milliseconds scheduleInterval); + void run(const message_callback &send); +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_PACING_HANDLER_H diff --git a/libdatachannel/include/rtc/peerconnection.hpp b/libdatachannel/include/rtc/peerconnection.hpp new file mode 100644 index 0000000..f7a76a1 --- /dev/null +++ b/libdatachannel/include/rtc/peerconnection.hpp @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_PEER_CONNECTION_H +#define RTC_PEER_CONNECTION_H + +#include "candidate.hpp" +#include "common.hpp" +#include "configuration.hpp" +#include "datachannel.hpp" +#include "description.hpp" +#include "reliability.hpp" +#include "track.hpp" + +#include +#include + +namespace rtc { + +namespace impl { + +struct PeerConnection; + +} + +struct RTC_CPP_EXPORT DataChannelInit { + Reliability reliability = {}; + bool negotiated = false; + optional id = nullopt; + string protocol = ""; +}; + +struct RTC_CPP_EXPORT LocalDescriptionInit { + optional iceUfrag; + optional icePwd; +}; + +class RTC_CPP_EXPORT PeerConnection final : CheshireCat { +public: + enum class State : int { + New = RTC_NEW, + Connecting = RTC_CONNECTING, + Connected = RTC_CONNECTED, + Disconnected = RTC_DISCONNECTED, + Failed = RTC_FAILED, + Closed = RTC_CLOSED + }; + + enum class IceState : int { + New = RTC_ICE_NEW, + Checking = RTC_ICE_CHECKING, + Connected = RTC_ICE_CONNECTED, + Completed = RTC_ICE_COMPLETED, + Failed = RTC_ICE_FAILED, + Disconnected = RTC_ICE_DISCONNECTED, + Closed = RTC_ICE_CLOSED + }; + + enum class GatheringState : int { + New = RTC_GATHERING_NEW, + InProgress = RTC_GATHERING_INPROGRESS, + Complete = RTC_GATHERING_COMPLETE + }; + + enum class SignalingState : int { + Stable = RTC_SIGNALING_STABLE, + HaveLocalOffer = RTC_SIGNALING_HAVE_LOCAL_OFFER, + HaveRemoteOffer = RTC_SIGNALING_HAVE_REMOTE_OFFER, + HaveLocalPranswer = RTC_SIGNALING_HAVE_LOCAL_PRANSWER, + HaveRemotePranswer = RTC_SIGNALING_HAVE_REMOTE_PRANSWER, + }; + + PeerConnection(); + PeerConnection(Configuration config); + ~PeerConnection(); + + void close(); + + const Configuration *config() const; + State state() const; + IceState iceState() const; + GatheringState gatheringState() const; + SignalingState signalingState() const; + bool negotiationNeeded() const; + bool hasMedia() const; + optional localDescription() const; + optional remoteDescription() const; + size_t remoteMaxMessageSize() const; + optional localAddress() const; + optional remoteAddress() const; + uint16_t maxDataChannelId() const; + bool getSelectedCandidatePair(Candidate *local, Candidate *remote); + + void setLocalDescription(Description::Type type = Description::Type::Unspec, LocalDescriptionInit init = {}); + void gatherLocalCandidates(std::vector additionalIceServers = {}); + void setRemoteDescription(Description description); + void addRemoteCandidate(Candidate candidate); + + // For specific use cases only + Description createOffer(); + Description createAnswer(); + + void setMediaHandler(shared_ptr handler); + shared_ptr getMediaHandler(); + + [[nodiscard]] shared_ptr createDataChannel(string label, + DataChannelInit init = {}); + void onDataChannel(std::function dataChannel)> callback); + + [[nodiscard]] shared_ptr addTrack(Description::Media description); + void onTrack(std::function track)> callback); + + void onLocalDescription(std::function callback); + void onLocalCandidate(std::function callback); + void onStateChange(std::function callback); + void onIceStateChange(std::function callback); + void onGatheringStateChange(std::function callback); + void onSignalingStateChange(std::function callback); + + void resetCallbacks(); + CertificateFingerprint remoteFingerprint(); + + // Stats + void clearStats(); + size_t bytesSent(); + size_t bytesReceived(); + optional rtt(); +}; + +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::State state); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::IceState state); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::GatheringState state); +RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::SignalingState state); + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/plihandler.hpp b/libdatachannel/include/rtc/plihandler.hpp new file mode 100644 index 0000000..91eeaf9 --- /dev/null +++ b/libdatachannel/include/rtc/plihandler.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2023 Arda Cinar + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_PLI_RESPONDER_H +#define RTC_PLI_RESPONDER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "utils.hpp" + +#include + +namespace rtc { + +/// Responds to PLI and FIR messages sent by the receiver. The sender should respond to these +/// messages by sending an intra. +class RTC_CPP_EXPORT PliHandler final : public MediaHandler { + rtc::synchronized_callback<> mOnPli; + +public: + /// Constructs the PLIResponder object to notify whenever a new intra frame is requested + /// @param onPli The callback that gets called whenever an intra frame is requested by the receiver + PliHandler(std::function onPli); + + void incoming(message_vector &messages, const message_callback &send) override; + +protected: + std::unordered_map mFirSSRCSeqNumberMap; +}; + +} + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_PLI_RESPONDER_H diff --git a/libdatachannel/include/rtc/reliability.hpp b/libdatachannel/include/rtc/reliability.hpp new file mode 100644 index 0000000..df63b93 --- /dev/null +++ b/libdatachannel/include/rtc/reliability.hpp @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RELIABILITY_H +#define RTC_RELIABILITY_H + +#include "common.hpp" + +#include + +namespace rtc { + +struct Reliability { + // It true, the channel does not enforce message ordering and out-of-order delivery is allowed + bool unordered = false; + + // If both maxPacketLifeTime or maxRetransmits are unset, the channel is reliable. + // If either maxPacketLifeTime or maxRetransmits is set, the channel is unreliable. + // (The settings are exclusive so both maxPacketLifetime and maxRetransmits must not be set.) + + // Time window during which transmissions and retransmissions may occur + optional maxPacketLifeTime; + + // Maximum number of retransmissions that are attempted + optional maxRetransmits; + + // For backward compatibility, do not use + enum class Type { Reliable = 0, Rexmit, Timed }; + union { + Type typeDeprecated = Type::Reliable; + [[deprecated("Use maxPacketLifeTime or maxRetransmits")]] Type type; + }; + variant rexmit = 0; +}; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/rembhandler.hpp b/libdatachannel/include/rtc/rembhandler.hpp new file mode 100644 index 0000000..6f98878 --- /dev/null +++ b/libdatachannel/include/rtc/rembhandler.hpp @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024 Vladimir Voronin + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_REMB_RESPONDER_H +#define RTC_REMB_RESPONDER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "utils.hpp" + +namespace rtc { + +/// Responds to REMB messages sent by the receiver. +class RTC_CPP_EXPORT RembHandler final : public MediaHandler { + rtc::synchronized_callback mOnRemb; + +public: + /// Constructs the RembResponder object to notify whenever a bitrate + /// @param onRemb The callback that gets called whenever a bitrate by the receiver + RembHandler(std::function onRemb); + + void incoming(message_vector &messages, const message_callback &send) override; +}; + +} + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_REMB_RESPONDER_H diff --git a/libdatachannel/include/rtc/rtc.h b/libdatachannel/include/rtc/rtc.h new file mode 100644 index 0000000..dddcaa5 --- /dev/null +++ b/libdatachannel/include/rtc/rtc.h @@ -0,0 +1,581 @@ +/** + * Copyright (c) 2019-2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_C_API +#define RTC_C_API + +#include "version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef RTC_STATIC +#define RTC_C_EXPORT +#else // dynamic library +#ifdef _WIN32 +#ifdef RTC_EXPORTS +#define RTC_C_EXPORT __declspec(dllexport) // building the library +#else +#define RTC_C_EXPORT __declspec(dllimport) // using the library +#endif +#else // not WIN32 +#define RTC_C_EXPORT +#endif +#endif + +#ifndef RTC_ENABLE_WEBSOCKET +#define RTC_ENABLE_WEBSOCKET 1 +#endif + +#ifndef RTC_ENABLE_MEDIA +#define RTC_ENABLE_MEDIA 1 +#endif + +#define RTC_DEFAULT_MTU 1280 // IPv6 minimum guaranteed MTU + +#if RTC_ENABLE_MEDIA +#define RTC_DEFAULT_MAX_FRAGMENT_SIZE ((uint16_t)(RTC_DEFAULT_MTU - 12 - 8 - 40)) // SRTP/UDP/IPv6 +#define RTC_DEFAULT_MAX_STORED_PACKET_COUNT 512 +// Deprecated, do not use +#define RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE RTC_DEFAULT_MAX_FRAGMENT_SIZE +#define RTC_DEFAULT_MAXIMUM_PACKET_COUNT_FOR_NACK_CACHE RTC_DEFAULT_MAX_STORED_PACKET_COUNT +#endif + +#ifdef _WIN32 +#ifdef CAPI_STDCALL +#define RTC_API __stdcall +#else +#define RTC_API +#endif +#else // not WIN32 +#define RTC_API +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define RTC_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define RTC_DEPRECATED __declspec(deprecated) +#else +#define DEPRECATED +#endif + +// libdatachannel C API + +typedef enum { + RTC_NEW = 0, + RTC_CONNECTING = 1, + RTC_CONNECTED = 2, + RTC_DISCONNECTED = 3, + RTC_FAILED = 4, + RTC_CLOSED = 5 +} rtcState; + +typedef enum { + RTC_ICE_NEW = 0, + RTC_ICE_CHECKING = 1, + RTC_ICE_CONNECTED = 2, + RTC_ICE_COMPLETED = 3, + RTC_ICE_FAILED = 4, + RTC_ICE_DISCONNECTED = 5, + RTC_ICE_CLOSED = 6 +} rtcIceState; + +typedef enum { + RTC_GATHERING_NEW = 0, + RTC_GATHERING_INPROGRESS = 1, + RTC_GATHERING_COMPLETE = 2 +} rtcGatheringState; + +typedef enum { + RTC_SIGNALING_STABLE = 0, + RTC_SIGNALING_HAVE_LOCAL_OFFER = 1, + RTC_SIGNALING_HAVE_REMOTE_OFFER = 2, + RTC_SIGNALING_HAVE_LOCAL_PRANSWER = 3, + RTC_SIGNALING_HAVE_REMOTE_PRANSWER = 4, +} rtcSignalingState; + +typedef enum { // Don't change, it must match plog severity + RTC_LOG_NONE = 0, + RTC_LOG_FATAL = 1, + RTC_LOG_ERROR = 2, + RTC_LOG_WARNING = 3, + RTC_LOG_INFO = 4, + RTC_LOG_DEBUG = 5, + RTC_LOG_VERBOSE = 6 +} rtcLogLevel; + +typedef enum { + RTC_CERTIFICATE_DEFAULT = 0, // ECDSA + RTC_CERTIFICATE_ECDSA = 1, + RTC_CERTIFICATE_RSA = 2, +} rtcCertificateType; + +typedef enum { + // video + RTC_CODEC_H264 = 0, + RTC_CODEC_VP8 = 1, + RTC_CODEC_VP9 = 2, + RTC_CODEC_H265 = 3, + RTC_CODEC_AV1 = 4, + + // audio + RTC_CODEC_OPUS = 128, + RTC_CODEC_PCMU = 129, + RTC_CODEC_PCMA = 130, + RTC_CODEC_AAC = 131, + RTC_CODEC_G722 = 132, +} rtcCodec; + +typedef enum { + RTC_DIRECTION_UNKNOWN = 0, + RTC_DIRECTION_SENDONLY = 1, + RTC_DIRECTION_RECVONLY = 2, + RTC_DIRECTION_SENDRECV = 3, + RTC_DIRECTION_INACTIVE = 4 +} rtcDirection; + +typedef enum { RTC_TRANSPORT_POLICY_ALL = 0, RTC_TRANSPORT_POLICY_RELAY = 1 } rtcTransportPolicy; + +#define RTC_ERR_SUCCESS 0 +#define RTC_ERR_INVALID -1 // invalid argument +#define RTC_ERR_FAILURE -2 // runtime error +#define RTC_ERR_NOT_AVAIL -3 // element not available +#define RTC_ERR_TOO_SMALL -4 // buffer too small + +typedef void(RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message); +typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const char *type, + void *ptr); +typedef void(RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid, + void *ptr); +typedef void(RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState state, void *ptr); +typedef void(RTC_API *rtcIceStateChangeCallbackFunc)(int pc, rtcIceState state, void *ptr); +typedef void(RTC_API *rtcGatheringStateCallbackFunc)(int pc, rtcGatheringState state, void *ptr); +typedef void(RTC_API *rtcSignalingStateCallbackFunc)(int pc, rtcSignalingState state, void *ptr); +typedef void(RTC_API *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr); +typedef void(RTC_API *rtcTrackCallbackFunc)(int pc, int tr, void *ptr); +typedef void(RTC_API *rtcOpenCallbackFunc)(int id, void *ptr); +typedef void(RTC_API *rtcClosedCallbackFunc)(int id, void *ptr); +typedef void(RTC_API *rtcErrorCallbackFunc)(int id, const char *error, void *ptr); +typedef void(RTC_API *rtcMessageCallbackFunc)(int id, const char *message, int size, void *ptr); +typedef void *(RTC_API *rtcInterceptorCallbackFunc)(int pc, const char *message, int size, + void *ptr); +typedef void(RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr); +typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr); +typedef void(RTC_API *rtcPliHandlerCallbackFunc)(int tr, void *ptr); +typedef void(RTC_API *rtcRembHandlerCallbackFunc)(int tr, unsigned int bitrate, void *ptr); + +typedef struct { + uint32_t timestamp; + uint8_t payloadType; + double timestampSeconds; // negative means not available +} rtcFrameInfo; + +typedef void(RTC_API *rtcFrameCallbackFunc)(int tr, const char *data, int size, + const rtcFrameInfo *info, void *ptr); + +// Log + +// NULL cb on the first call will log to stdout +RTC_C_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); + +// User pointer +RTC_C_EXPORT void rtcSetUserPointer(int id, void *ptr); +RTC_C_EXPORT void *rtcGetUserPointer(int i); + +// PeerConnection + +typedef struct { + const char **iceServers; + int iceServersCount; + const char *proxyServer; // libnice only + const char *bindAddress; // libjuice only, NULL means any + rtcCertificateType certificateType; + const char *certificatePemFile; // NULL for autogenerated certificate + const char *keyPemFile; // NULL for autogenerated certificate + const char *keyPemPass; // NULL if no pass + rtcTransportPolicy iceTransportPolicy; + bool enableIceTcp; + bool enableIceUdpMux; // libjuice only + bool disableAutoNegotiation; + bool forceMediaTransport; + uint16_t portRangeBegin; // 0 means automatic + uint16_t portRangeEnd; // 0 means automatic + int mtu; // <= 0 means automatic + int maxMessageSize; // <= 0 means default +} rtcConfiguration; + +RTC_C_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id +RTC_C_EXPORT int rtcClosePeerConnection(int pc); +RTC_C_EXPORT int rtcDeletePeerConnection(int pc); + +RTC_C_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb); +RTC_C_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb); +RTC_C_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb); +RTC_C_EXPORT int rtcSetIceStateChangeCallback(int pc, rtcIceStateChangeCallbackFunc cb); +RTC_C_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb); +RTC_C_EXPORT int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb); + +RTC_C_EXPORT int rtcSetLocalDescription(int pc, const char *type); // type may be NULL +RTC_C_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type); +RTC_C_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid); + +RTC_C_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size); +RTC_C_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size); + +RTC_C_EXPORT int rtcGetLocalDescriptionType(int pc, char *buffer, int size); +RTC_C_EXPORT int rtcGetRemoteDescriptionType(int pc, char *buffer, int size); + +// For specific use cases only +RTC_C_EXPORT int rtcCreateOffer(int pc, char *buffer, int size); +RTC_C_EXPORT int rtcCreateAnswer(int pc, char *buffer, int size); + +RTC_C_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size); +RTC_C_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size); + +RTC_C_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, + int remoteSize); + +RTC_C_EXPORT bool rtcIsNegotiationNeeded(int pc); + +RTC_C_EXPORT int rtcGetMaxDataChannelStream(int pc); +RTC_C_EXPORT int rtcGetRemoteMaxMessageSize(int pc); + +// DataChannel, Track, and WebSocket common API + +RTC_C_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb); +RTC_C_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb); +RTC_C_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb); +RTC_C_EXPORT int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb); +RTC_C_EXPORT int rtcSendMessage(int id, const char *data, int size); +RTC_C_EXPORT int rtcClose(int id); +RTC_C_EXPORT int rtcDelete(int id); +RTC_C_EXPORT bool rtcIsOpen(int id); +RTC_C_EXPORT bool rtcIsClosed(int id); + +RTC_C_EXPORT int rtcMaxMessageSize(int id); +RTC_C_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send +RTC_C_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount); +RTC_C_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb); + +// DataChannel, Track, and WebSocket common extended API + +RTC_C_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive +RTC_C_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb); +RTC_C_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size); + +// DataChannel + +typedef struct { + bool unordered; + bool unreliable; + unsigned int maxPacketLifeTime; // ignored if reliable + unsigned int maxRetransmits; // ignored if reliable +} rtcReliability; + +typedef struct { + rtcReliability reliability; + const char *protocol; // empty string if NULL + bool negotiated; + bool manualStream; + uint16_t stream; // numeric ID 0-65534, ignored if manualStream is false +} rtcDataChannelInit; + +RTC_C_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb); +RTC_C_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id +RTC_C_EXPORT int rtcCreateDataChannelEx(int pc, const char *label, + const rtcDataChannelInit *init); // returns dc id +RTC_C_EXPORT int rtcDeleteDataChannel(int dc); + +RTC_C_EXPORT int rtcGetDataChannelStream(int dc); +RTC_C_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size); +RTC_C_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size); +RTC_C_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability); + +// Track + +typedef struct { + rtcDirection direction; + rtcCodec codec; + int payloadType; + uint32_t ssrc; + const char *mid; + const char *name; // optional + const char *msid; // optional + const char *trackId; // optional, track ID used in MSID + const char *profile; // optional, codec profile +} rtcTrackInit; + +RTC_C_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb); +RTC_C_EXPORT int rtcAddTrack(int pc, const char *mediaDescriptionSdp); // returns tr id +RTC_C_EXPORT int rtcAddTrackEx(int pc, const rtcTrackInit *init); // returns tr id +RTC_C_EXPORT int rtcDeleteTrack(int tr); + +RTC_C_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size); +RTC_C_EXPORT int rtcGetTrackMid(int tr, char *buffer, int size); +RTC_C_EXPORT int rtcGetTrackDirection(int tr, rtcDirection *direction); + +RTC_C_EXPORT int rtcRequestKeyframe(int tr); +RTC_C_EXPORT int rtcRequestBitrate(int tr, unsigned int bitrate); + +RTC_C_EXPORT int rtcSetFrameCallback(int tr, rtcFrameCallbackFunc cb); + +#if RTC_ENABLE_MEDIA + +// Media + +// Define how OBUs are packetizied in a AV1 Sample +typedef enum { + RTC_OBU_PACKETIZED_OBU = 0, + RTC_OBU_PACKETIZED_TEMPORAL_UNIT = 1, +} rtcObuPacketization; + +// Define how NAL units are separated in a H264/H265 sample +typedef enum { + RTC_NAL_SEPARATOR_LENGTH = 0, // first 4 bytes are NAL unit length + RTC_NAL_SEPARATOR_LONG_START_SEQUENCE = 1, // 0x00, 0x00, 0x00, 0x01 + RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE = 2, // 0x00, 0x00, 0x01 + RTC_NAL_SEPARATOR_START_SEQUENCE = 3, // long or short start sequence +} rtcNalUnitSeparator; + +typedef struct { + uint32_t ssrc; + const char *cname; + uint8_t payloadType; + uint32_t clockRate; + uint16_t sequenceNumber; + uint32_t timestamp; + + // H264, H265, AV1 + uint16_t maxFragmentSize; // Maximum fragment size, 0 means default + + // H264/H265 only + rtcNalUnitSeparator nalSeparator; // NAL unit separator + + // AV1 only + rtcObuPacketization obuPacketization; // OBU paketization for AV1 samples + + uint8_t playoutDelayId; + uint16_t playoutDelayMin; + uint16_t playoutDelayMax; + + uint8_t colorSpaceId; + uint8_t colorChromaSitingHorz; + uint8_t colorChromaSitingVert; + uint8_t colorRange; + uint8_t colorPrimaries; + uint8_t colorTransfer; + uint8_t colorMatrix; +} rtcPacketizerInit; + +// Deprecated, do not use +typedef rtcPacketizerInit rtcPacketizationHandlerInit; + +typedef struct { + uint32_t ssrc; + const char *name; // optional + const char *msid; // optional + const char *trackId; // optional, track ID used in MSID +} rtcSsrcForTypeInit; + +// Opaque type used (via rtcMessage*) to reference an rtc::Message +typedef void *rtcMessage; + +// Allocate a new opaque message. +// Must be explicitly freed by rtcDeleteOpaqueMessage() unless +// explicitly returned by a media interceptor callback; +RTC_C_EXPORT rtcMessage *rtcCreateOpaqueMessage(void *data, int size); +RTC_C_EXPORT void rtcDeleteOpaqueMessage(rtcMessage *msg); + +// Set MediaInterceptor on peer connection +RTC_C_EXPORT int rtcSetMediaInterceptorCallback(int id, rtcInterceptorCallbackFunc cb); + +// Set a packetizer on track +RTC_C_EXPORT int rtcSetH264Packetizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetH265Packetizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetAV1Packetizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetVP8Packetizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetVP9Packetizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetOpusPacketizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetAACPacketizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetPCMUPacketizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetPCMAPacketizer(int tr, const rtcPacketizerInit *init); +RTC_C_EXPORT int rtcSetG722Packetizer(int tr, const rtcPacketizerInit *init); + +// Set a depacketizer on track +RTC_C_EXPORT int rtcSetH264Depacketizer(int tr, rtcNalUnitSeparator nalSeparator); +RTC_C_EXPORT int rtcSetH265Depacketizer(int tr, rtcNalUnitSeparator nalSeparator); +RTC_C_EXPORT int rtcSetVP8Depacketizer(int tr); +RTC_C_EXPORT int rtcSetVP9Depacketizer(int tr); +RTC_C_EXPORT int rtcSetOpusDepacketizer(int tr); +RTC_C_EXPORT int rtcSetAACDepacketizer(int tr); +RTC_C_EXPORT int rtcSetPCMUDepacketizer(int tr); +RTC_C_EXPORT int rtcSetPCMADepacketizer(int tr); +RTC_C_EXPORT int rtcSetG722Depacketizer(int tr); + +// Deprecated, do not use +RTC_DEPRECATED static inline int +rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { + return rtcSetH264Packetizer(tr, init); +} +RTC_DEPRECATED static inline int +rtcSetH265PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { + return rtcSetH265Packetizer(tr, init); +} +RTC_DEPRECATED static inline int +rtcSetAV1PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { + return rtcSetAV1Packetizer(tr, init); +} +RTC_DEPRECATED static inline int +rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { + return rtcSetOpusPacketizer(tr, init); +} +RTC_DEPRECATED static inline int +rtcSetAACPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { + return rtcSetAACPacketizer(tr, init); +} + +// Chain RtcpReceivingSession on track +RTC_C_EXPORT int rtcChainRtcpReceivingSession(int tr); + +// Chain RtcpSrReporter on track +RTC_C_EXPORT int rtcChainRtcpSrReporter(int tr); + +// Chain RtcpNackResponder on track +RTC_C_EXPORT int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount); + +// Chain PliHandler on track +RTC_C_EXPORT int rtcChainPliHandler(int tr, rtcPliHandlerCallbackFunc cb); + +// Chain RembHandler on track +RTC_C_EXPORT int rtcChainRembHandler(int tr, rtcRembHandlerCallbackFunc cb); + +// Chain PacingHandler on track +RTC_C_EXPORT int rtcChainPacingHandler(int tr, double bitsPerSecond, int sendIntervalMs); + +// Transform seconds to timestamp using track's clock rate, result is written to timestamp +RTC_C_EXPORT int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp); + +// Transform timestamp to seconds using track's clock rate, result is written to seconds +RTC_C_EXPORT int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds); + +// Get current timestamp, result is written to timestamp +RTC_C_EXPORT int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp); + +// Set RTP timestamp for track identified by given id +RTC_C_EXPORT int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp); + +// Get timestamp of last RTCP SR, result is written to timestamp +RTC_C_EXPORT int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp); + +// Get sync timestamps from receiving RTCP session +RTC_C_EXPORT int rtcGetTrackRtcpSyncTimestamps(int tr, uint64_t *rtpTimestamp, uint64_t *ntpTimestamp); + +// Get all available payload types for given codec and stores them in buffer, does nothing if +// buffer is NULL +RTC_C_EXPORT int rtcGetTrackPayloadTypesForCodec(int tr, const char *ccodec, int *buffer, int size); + +// Get all SSRCs for given track +RTC_C_EXPORT int rtcGetSsrcsForTrack(int tr, uint32_t *buffer, int count); + +// Get CName for SSRC +RTC_C_EXPORT int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char *cname, int cnameSize); + +// Get all SSRCs for given media type in given SDP +RTC_C_EXPORT int rtcGetSsrcsForType(const char *mediaType, const char *sdp, uint32_t *buffer, int bufferSize); + +// Set SSRC for given media type in given SDP +RTC_C_EXPORT int rtcSetSsrcForType(const char *mediaType, const char *sdp, char *buffer, const int bufferSize, + rtcSsrcForTypeInit *init); + +// For backward compatibility, do not use +RTC_C_EXPORT RTC_DEPRECATED int rtcSetNeedsToSendRtcpSr(int id); + +#endif // RTC_ENABLE_MEDIA + +#if RTC_ENABLE_WEBSOCKET + +// WebSocket + +typedef struct { + bool disableTlsVerification; // if true, don't verify the TLS certificate + const char *proxyServer; // only non-authenticated http supported for now + const char **protocols; + int protocolsCount; + int tcpConnectionTimeoutMs; // in milliseconds, 0 means default, < 0 means disabled + int connectionTimeoutMs; // in milliseconds, 0 means default, < 0 means disabled + int pingIntervalMs; // in milliseconds, 0 means default, < 0 means disabled + int maxOutstandingPings; // 0 means default, < 0 means disabled + int maxMessageSize; // <= 0 means default +} rtcWsConfiguration; + +RTC_C_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id +RTC_C_EXPORT int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config); +RTC_C_EXPORT int rtcDeleteWebSocket(int ws); + +RTC_C_EXPORT int rtcGetWebSocketRemoteAddress(int ws, char *buffer, int size); +RTC_C_EXPORT int rtcGetWebSocketPath(int ws, char *buffer, int size); + +// WebSocketServer + +typedef void(RTC_API *rtcWebSocketClientCallbackFunc)(int wsserver, int ws, void *ptr); + +typedef struct { + uint16_t port; // 0 means automatic selection + bool enableTls; // if true, enable TLS (WSS) + const char *certificatePemFile; // NULL for autogenerated certificate + const char *keyPemFile; // NULL for autogenerated certificate + const char *keyPemPass; // NULL if no pass + const char *bindAddress; // NULL for any + int connectionTimeoutMs; // in milliseconds, 0 means default, < 0 means disabled + int maxMessageSize; // <= 0 means default +} rtcWsServerConfiguration; + +RTC_C_EXPORT int rtcCreateWebSocketServer(const rtcWsServerConfiguration *config, + rtcWebSocketClientCallbackFunc cb); // returns wsserver id +RTC_C_EXPORT int rtcDeleteWebSocketServer(int wsserver); + +RTC_C_EXPORT int rtcGetWebSocketServerPort(int wsserver); + +#endif + +// Global settings + +// Note: Applied when threads are spawned +RTC_C_EXPORT int rtcSetThreadPoolSize(unsigned int count); + +typedef struct { + int recvBufferSize; // in bytes, <= 0 means optimized default + int sendBufferSize; // in bytes, <= 0 means optimized default + int maxChunksOnQueue; // in chunks, <= 0 means optimized default + int initialCongestionWindow; // in MTUs, <= 0 means optimized default + int maxBurst; // in MTUs, 0 means optimized default, < 0 means disabled + int congestionControlModule; // 0: RFC2581 (default), 1: HSTCP, 2: H-TCP, 3: RTCC + int delayedSackTimeMs; // in milliseconds, 0 means optimized default, < 0 means disabled + int minRetransmitTimeoutMs; // in milliseconds, <= 0 means optimized default + int maxRetransmitTimeoutMs; // in milliseconds, <= 0 means optimized default + int initialRetransmitTimeoutMs; // in milliseconds, <= 0 means optimized default + int maxRetransmitAttempts; // number of retransmissions, <= 0 means optimized default + int heartbeatIntervalMs; // in milliseconds, <= 0 means optimized default +} rtcSctpSettings; + +// Note: SCTP settings apply to newly-created PeerConnections only +RTC_C_EXPORT int rtcSetSctpSettings(const rtcSctpSettings *settings); + +// Optional global preload and cleanup +RTC_C_EXPORT bool rtcPreload(void); +RTC_C_EXPORT void rtcCleanup(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/libdatachannel/include/rtc/rtc.hpp b/libdatachannel/include/rtc/rtc.hpp new file mode 100644 index 0000000..2d2a106 --- /dev/null +++ b/libdatachannel/include/rtc/rtc.hpp @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// C API +#include "rtc.h" + +// C++ API +#include "common.hpp" +#include "global.hpp" +// +#include "datachannel.hpp" +#include "iceudpmuxlistener.hpp" +#include "peerconnection.hpp" +#include "track.hpp" + +#if RTC_ENABLE_WEBSOCKET + +// WebSocket +#include "websocket.hpp" +#include "websocketserver.hpp" + +#endif // RTC_ENABLE_WEBSOCKET + +#if RTC_ENABLE_MEDIA + +// Media +#include "av1rtppacketizer.hpp" +#include "dependencydescriptor.hpp" +#include "rtppacketizer.hpp" +#include "rtpdepacketizer.hpp" +#include "h264rtppacketizer.hpp" +#include "h264rtpdepacketizer.hpp" +#include "h265rtppacketizer.hpp" +#include "h265rtpdepacketizer.hpp" +#include "vp8rtppacketizer.hpp" +#include "vp8rtpdepacketizer.hpp" +#include "vp9rtppacketizer.hpp" +#include "vp9rtpdepacketizer.hpp" +#include "mediahandler.hpp" +#include "rtcpapphandler.hpp" +#include "plihandler.hpp" +#include "rembhandler.hpp" +#include "pacinghandler.hpp" +#include "rtcpnackresponder.hpp" +#include "rtcpreceivingsession.hpp" +#include "rtcpsrreporter.hpp" + +#endif // RTC_ENABLE_MEDIA diff --git a/libdatachannel/include/rtc/rtcpapphandler.hpp b/libdatachannel/include/rtc/rtcpapphandler.hpp new file mode 100644 index 0000000..30dbef6 --- /dev/null +++ b/libdatachannel/include/rtc/rtcpapphandler.hpp @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026 Apple Inc + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTCP_APP_HANDLER_H +#define RTC_RTCP_APP_HANDLER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "rtp.hpp" +#include "utils.hpp" + +namespace rtc { + +/// Handles RTCP APP packets (payload type 204) as defined in RFC 3550 section 6.7. +class RTC_CPP_EXPORT RtcpAppHandler final : public MediaHandler { + rtc::synchronized_callback mOnApp; + +public: + /// Constructs the RtcpAppHandler to notify when RTCP APP packets are received. + /// @param onApp Callback invoked with (name, subtype, application-dependent data) + RtcpAppHandler(std::function onApp); + + void incoming(message_vector &messages, const message_callback &send) override; +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_RTCP_APP_HANDLER_H diff --git a/libdatachannel/include/rtc/rtcpnackresponder.hpp b/libdatachannel/include/rtc/rtcpnackresponder.hpp new file mode 100644 index 0000000..045d956 --- /dev/null +++ b/libdatachannel/include/rtc/rtcpnackresponder.hpp @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTCP_NACK_RESPONDER_H +#define RTC_RTCP_NACK_RESPONDER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "description.hpp" +#include "rtp.hpp" + +#include +#include +#include + +namespace rtc { + +class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandler { +public: + static const size_t DefaultMaxSize = 512; + + RtcpNackResponder(size_t maxSize = DefaultMaxSize); + + void media(const Description::Media &desc) override; + void incoming(message_vector &messages, const message_callback &send) override; + void outgoing(message_vector &messages, const message_callback &send) override; + +private: + message_ptr wrapInRtx(const message_ptr &original); + + // RTX state populated by media() from SDP inspection + optional mRtxSsrc; + std::unordered_map mRtxPayloadTypeMap; // original PT -> RTX PT + uint16_t mRtxSequenceNumber = 0; + bool mRtxEnabled = false; + std::mutex mMutex; + + // Packet storage + class RTC_CPP_EXPORT Storage { + + /// Packet storage element + struct RTC_CPP_EXPORT Element { + Element(message_ptr packet, uint16_t sequenceNumber, shared_ptr next = nullptr); + const message_ptr packet; + const uint16_t sequenceNumber; + /// Pointer to newer element + shared_ptr next = nullptr; + }; + + private: + /// Oldest packet in storage + shared_ptr oldest = nullptr; + /// Newest packet in storage + shared_ptr newest = nullptr; + /// Inner storage + std::unordered_map> storage{}; + std::mutex mutex; + + /// Maximum storage size + const size_t maxSize; + + /// Returns current size + size_t size(); + + public: + Storage(size_t _maxSize); + + /// Returns packet with given sequence number + message_ptr get(uint16_t sequenceNumber); + + /// Stores packet + /// @param packet Packet + void store(message_ptr packet); + }; + + const shared_ptr mStorage; +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_RTCP_NACK_RESPONDER_H */ diff --git a/libdatachannel/include/rtc/rtcpreceivingsession.hpp b/libdatachannel/include/rtc/rtcpreceivingsession.hpp new file mode 100644 index 0000000..e1e25f2 --- /dev/null +++ b/libdatachannel/include/rtc/rtcpreceivingsession.hpp @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTCP_RECEIVING_SESSION_H +#define RTC_RTCP_RECEIVING_SESSION_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "description.hpp" +#include "mediahandler.hpp" +#include "message.hpp" +#include "rtp.hpp" + +#include +#include +#include +#include + +#define RTP_SEQ_MOD (1<<16) + +namespace rtc { + +// An RtcpSession can be plugged into a Track to handle the whole RTCP session +class RTC_CPP_EXPORT RtcpReceivingSession : public MediaHandler { +public: + RtcpReceivingSession() = default; + virtual ~RtcpReceivingSession() = default; + + void media(const Description::Media &desc) override; + void incoming(message_vector &messages, const message_callback &send) override; + bool requestKeyframe(const std::vector& targetSSRCs, bool retransmit, const message_callback &send) override; + bool requestBitrate(unsigned int bitrate, const message_callback &send) override; + + // For backward compatibility + [[deprecated("Use Track::requestKeyframe()")]] inline bool requestKeyframe() { return false; }; + [[deprecated("Use Track::requestBitrate()")]] inline void requestBitrate(unsigned int) {}; + + struct SyncTimestamps { + uint64_t rtpTimestamp; + uint64_t ntpTimestamp; + }; + + SyncTimestamps getSyncTimestamps(); + +protected: + void pushREMB(const message_callback &send, unsigned int bitrate); + void pushRR(const message_callback &send,unsigned int lastSrDelay); + void pushPLI(const message_callback &send); + void pushFIR(const message_callback &send, const std::vector& targetSSRCs, bool retransmit); + + void initSeq(uint16_t seq); + bool updateSeq(uint16_t seq); + + SSRC mSsrc = 0; + uint32_t mGreatestSeqNo = 0; + uint16_t mMaxSeq = 0; // highest seq. number seen + uint32_t mCycles = 0; // shifted count of seq. number cycles + uint32_t mBaseSeq = 0; // base seq number + uint32_t mBadSeq = 0; // last 'bad' seq number + 1 + uint32_t mProbation = 0; // sequ. packets till source is valid + uint32_t mReceived = 0; // packets received + uint32_t mExpectedPrior = 0; // packet expected at last interval + uint32_t mReceivedPrior = 0; // packet received at last interval + uint32_t mTransit = 0; // relative trans time for prev pkt + uint32_t mJitter = 0; + + SyncTimestamps mSyncTimestamps{0,0}; + + std::atomic mRequestedBitrate = 0; + std::mutex mSyncMutex; + std::mutex mMutex; + + message_ptr unwrapRtx(const message_ptr &rtxPacket); + + // RTX state populated by media() from SDP inspection + std::unordered_map mRtxToPrimaryPtMap; // RTX PT -> primary PT + SSRC mRtxPrimarySsrc = 0; + bool mRtxEnabled = false; + + bool mSupportsRfc5104Fir = false; + std::unordered_map mRfc5104FirCmdNums; // SSRC -> seq no mapping +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_RTCP_RECEIVING_SESSION_H diff --git a/libdatachannel/include/rtc/rtcpsrreporter.hpp b/libdatachannel/include/rtc/rtcpsrreporter.hpp new file mode 100644 index 0000000..70f05c2 --- /dev/null +++ b/libdatachannel/include/rtc/rtcpsrreporter.hpp @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTCP_SR_REPORTER_H +#define RTC_RTCP_SR_REPORTER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "rtp.hpp" +#include "rtppacketizationconfig.hpp" + +#include + +namespace rtc { + +class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandler { +public: + RtcpSrReporter(shared_ptr rtpConfig); + ~RtcpSrReporter(); + + uint32_t lastReportedTimestamp() const; + [[deprecated]] void setNeedsToReport(); + + void outgoing(message_vector &messages, const message_callback &send) override; + + // TODO: remove this + const shared_ptr rtpConfig; + +private: + void addToReport(RtpHeader *header, size_t size); + message_ptr getSenderReport(uint32_t timestamp); + + uint32_t mPacketCount = 0; + uint32_t mPayloadOctets = 0; + uint32_t mLastReportedTimestamp = 0; + std::chrono::steady_clock::time_point mLastReportTime; +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_RTCP_SR_REPORTER_H */ diff --git a/libdatachannel/include/rtc/rtp.hpp b/libdatachannel/include/rtc/rtp.hpp new file mode 100644 index 0000000..9e4c3ea --- /dev/null +++ b/libdatachannel/include/rtc/rtp.hpp @@ -0,0 +1,407 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020 Paul-Louis Ageneau + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTP_HPP +#define RTC_RTP_HPP + +#include "common.hpp" + +#include +#include + +namespace rtc { + +typedef uint32_t SSRC; + +RTC_CPP_EXPORT bool IsRtcp(const binary &data); + +#pragma pack(push, 1) + +struct RTC_CPP_EXPORT RtpExtensionHeader { + uint16_t _profileSpecificId; + uint16_t _headerLength; + + [[nodiscard]] uint16_t profileSpecificId() const; + [[nodiscard]] uint16_t headerLength() const; + + [[nodiscard]] size_t getSize() const; + [[nodiscard]] const char *getBody() const; + [[nodiscard]] char *getBody(); + + void setProfileSpecificId(uint16_t profileSpecificId); + void setHeaderLength(uint16_t headerLength); + + void clearBody(); + size_t writeCurrentVideoOrientation(bool twoByteHeader, size_t offset, uint8_t id, + uint8_t value); + size_t writeOneByteHeader(size_t offset, uint8_t id, const byte *value, size_t size); + size_t writeTwoByteHeader(size_t offset, uint8_t id, const byte *value, size_t size); + size_t writeHeader(bool twoByteHeader, size_t offset, uint8_t id, const byte *value, + size_t size); +}; + +struct RTC_CPP_EXPORT RtpHeader { + uint8_t _first; + uint8_t _payloadType; + uint16_t _seqNumber; + uint32_t _timestamp; + SSRC _ssrc; + // The following field is SSRC _csrc[] + + [[nodiscard]] uint8_t version() const; + [[nodiscard]] bool padding() const; + [[nodiscard]] bool extension() const; + [[nodiscard]] uint8_t csrcCount() const; + [[nodiscard]] uint8_t marker() const; + [[nodiscard]] uint8_t payloadType() const; + [[nodiscard]] uint16_t seqNumber() const; + [[nodiscard]] uint32_t timestamp() const; + [[nodiscard]] uint32_t ssrc() const; + + [[nodiscard]] size_t getSize() const; + [[nodiscard]] size_t getExtensionHeaderSize() const; + [[nodiscard]] const RtpExtensionHeader *getExtensionHeader() const; + [[nodiscard]] RtpExtensionHeader *getExtensionHeader(); + [[nodiscard]] const char *getBody() const; + [[nodiscard]] char *getBody(); + + void log() const; + + void preparePacket(); + void setSeqNumber(uint16_t newSeqNo); + void setPayloadType(uint8_t newPayloadType); + void setSsrc(uint32_t in_ssrc); + void setMarker(bool marker); + void setTimestamp(uint32_t i); + void setExtension(bool extension); +}; + +struct RTC_CPP_EXPORT RtcpReportBlock { + SSRC _ssrc; + uint32_t _fractionLostAndPacketsLost; // fraction lost is 8-bit, packets lost is 24-bit + uint16_t _seqNoCycles; + uint16_t _highestSeqNo; + uint32_t _jitter; + uint32_t _lastReport; + uint32_t _delaySinceLastReport; + + [[nodiscard]] uint16_t seqNoCycles() const; + [[nodiscard]] uint16_t highestSeqNo() const; + [[nodiscard]] uint32_t extendedHighestSeqNo() const; + [[nodiscard]] uint32_t jitter() const; + [[nodiscard]] uint32_t delaySinceSR() const; + + [[nodiscard]] SSRC getSSRC() const; + [[nodiscard]] uint32_t getNTPOfSR() const; + [[nodiscard]] uint8_t getFractionLost() const; + [[nodiscard]] unsigned int getPacketsLostCount() const; + + void preparePacket(SSRC in_ssrc, uint8_t fraction, unsigned int totalPacketsLost, + uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter, + uint64_t lastSR_NTP, uint64_t lastSR_DELAY); + void setSSRC(SSRC in_ssrc); + void setPacketsLost(uint8_t fractionLost, unsigned int packetsLostCount); + void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles); + void setJitter(uint32_t jitter); + void setNTPOfSR(uint64_t ntp); + void setDelaySinceSR(uint32_t sr); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpHeader { + uint8_t _first; + uint8_t _payloadType; + uint16_t _length; + + [[nodiscard]] uint8_t version() const; + [[nodiscard]] bool padding() const; + [[nodiscard]] uint8_t reportCount() const; + [[nodiscard]] uint8_t payloadType() const; + [[nodiscard]] uint16_t length() const; + [[nodiscard]] size_t lengthInBytes() const; + + void prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length); + void setPayloadType(uint8_t type); + void setReportCount(uint8_t count); + void setLength(uint16_t length); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpFbHeader { + RtcpHeader header; + + SSRC _packetSender; + SSRC _mediaSource; + + [[nodiscard]] SSRC packetSenderSSRC() const; + [[nodiscard]] SSRC mediaSourceSSRC() const; + + void setPacketSenderSSRC(SSRC ssrc); + void setMediaSourceSSRC(SSRC ssrc); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpSr { + RtcpHeader header; + + SSRC _senderSSRC; + uint64_t _ntpTimestamp; + uint32_t _rtpTimestamp; + uint32_t _packetCount; + uint32_t _octetCount; + + RtcpReportBlock _reportBlocks; + + [[nodiscard]] static unsigned int Size(unsigned int reportCount); + + [[nodiscard]] uint64_t ntpTimestamp() const; + [[nodiscard]] uint32_t rtpTimestamp() const; + [[nodiscard]] uint32_t packetCount() const; + [[nodiscard]] uint32_t octetCount() const; + [[nodiscard]] uint32_t senderSSRC() const; + + [[nodiscard]] const RtcpReportBlock *getReportBlock(int num) const; // nullptr if out-of-bounds + [[nodiscard]] RtcpReportBlock *getReportBlock(int num); // nullptr if out-of-bounds + [[nodiscard]] unsigned int size(unsigned int reportCount); + [[nodiscard]] size_t getSize() const; + + void preparePacket(SSRC senderSSRC, uint8_t reportCount); + void setNtpTimestamp(uint64_t ts); + void setRtpTimestamp(uint32_t ts); + void setOctetCount(uint32_t ts); + void setPacketCount(uint32_t ts); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpSdesItem { + uint8_t type; + + uint8_t _length; + char _text[1]; + + [[nodiscard]] static unsigned int Size(uint8_t textLength); + + [[nodiscard]] string text() const; + [[nodiscard]] uint8_t length() const; + + void setText(string text); +}; + +struct RTC_CPP_EXPORT RtcpSdesChunk { + SSRC _ssrc; + RtcpSdesItem _items; + + [[nodiscard]] static unsigned int Size(const std::vector textLengths); + + [[nodiscard]] SSRC ssrc() const; + + void setSSRC(SSRC ssrc); + + // Get item at given index + // All items with index < num must be valid, otherwise this function has undefined behaviour + // (use safelyCountChunkSize() to check if chunk is valid). + [[nodiscard]] const RtcpSdesItem *getItem(int num) const; + [[nodiscard]] RtcpSdesItem *getItem(int num); + + // Get size of chunk + // All items must be valid, otherwise this function has undefined behaviour (use + // safelyCountChunkSize() to check if chunk is valid) + [[nodiscard]] unsigned int getSize() const; + + long safelyCountChunkSize(size_t maxChunkSize) const; +}; + +struct RTC_CPP_EXPORT RtcpSdes { + RtcpHeader header; + RtcpSdesChunk _chunks; + + [[nodiscard]] static unsigned int Size(const std::vector> lengths); + + bool isValid() const; + + // Returns number of chunks in this packet + // Returns 0 if packet is invalid + unsigned int chunksCount() const; + + // Get chunk at given index + // All chunks (and their items) with index < `num` must be valid, otherwise this function has + // undefined behaviour (use `isValid` to check if chunk is valid). + const RtcpSdesChunk *getChunk(int num) const; + RtcpSdesChunk *getChunk(int num); + + void preparePacket(uint8_t chunkCount); +}; + +struct RTC_CPP_EXPORT RtcpRr { + RtcpHeader header; + + SSRC _senderSSRC; + RtcpReportBlock _reportBlocks; + + [[nodiscard]] static size_t SizeWithReportBlocks(uint8_t reportCount); + + SSRC senderSSRC() const; + bool isSenderReport(); + bool isReceiverReport(); + + [[nodiscard]] RtcpReportBlock *getReportBlock(int num); // nullptr if out-of-bounds + [[nodiscard]] const RtcpReportBlock *getReportBlock(int num) const; // nullptr if out-of-bounds + [[nodiscard]] size_t getSize() const; + + void preparePacket(SSRC senderSSRC, uint8_t reportCount); + void setSenderSSRC(SSRC ssrc); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpRemb { + RtcpFbHeader header; + + char _id[4]; // Unique identifier ('R' 'E' 'M' 'B') + uint32_t _bitrate; // Num SSRC, Br Exp, Br Mantissa (bit mask) + SSRC _ssrcs[1]; + + [[nodiscard]] static size_t SizeWithSSRCs(int count); + + [[nodiscard]] unsigned int getSize() const; + + void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate); + void setBitrate(unsigned int numSSRC, unsigned int in_bitrate); + SSRC getSSRC(int num) const; + void setSSRC(int num, SSRC newSsrc); + bool hasValidId() const; + int getSSRCCount() const; + unsigned int getBitrate() const; + + // Deprecated + [[deprecated("use setSSRC")]] void setSsrc(int num, SSRC newSssrc); + [[deprecated("use getSSRCCount")]] unsigned int getNumSSRC() const; + [[deprecated("use getSSRCCount")]] unsigned int getNumSSRC(); + unsigned int getBitrate(); +}; + +struct RTC_CPP_EXPORT RtcpPli { + RtcpFbHeader header; + + [[nodiscard]] static unsigned int Size(); + + void preparePacket(SSRC messageSSRC); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpFirFci { + uint32_t _ssrc; + uint8_t _seqNo; + uint8_t _dummy1; + uint16_t _dummy2; + + void preparePacket(SSRC ssrc, uint8_t seqNo); + + [[nodiscard]] SSRC getSSRC() const; + [[nodiscard]] uint8_t getSeqNo() const; +}; + +struct RTC_CPP_EXPORT RtcpFir { + RtcpFbHeader header; + RtcpFirFci _fcis[1]; + + [[nodiscard]] static size_t SizeWithFcis(int count); + + int getFciCount() const; + [[nodiscard]] const RtcpFirFci *getFci(int num) const; // nullptr if out-of-bounds + void preparePacket(SSRC senderSSRC, const std::vector &fcis); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtcpNackPart { + uint16_t _pid; + uint16_t _blp; + + uint16_t pid(); + uint16_t blp(); + + void setPid(uint16_t pid); + void setBlp(uint16_t blp); + + std::vector getSequenceNumbers(); +}; + +struct RTC_CPP_EXPORT RtcpNack { + RtcpFbHeader header; + RtcpNackPart parts[1]; + + [[nodiscard]] static unsigned int Size(unsigned int discreteSeqNoCount); + + [[nodiscard]] unsigned int getSeqNoCount(); + + void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount); + + /** + * Add a packet to the list of missing packets. + * @param fciCount The number of FCI fields that are present in this packet. + * Let the number start at zero and let this function grow the number. + * @param fciPID The seq no of the active FCI. It will be initialized automatically, and will + * change automatically. + * @param missingPacket The seq no of the missing packet. This will be added to the queue. + * @return true if the packet has grown, false otherwise. + */ + bool addMissingPacket(unsigned int *fciCount, uint16_t *fciPID, uint16_t missingPacket); +}; + +typedef std::array RtcpAppName; + +struct RTC_CPP_EXPORT RtcpApp { + RtcpHeader header; + + SSRC _ssrc; + char _name[4]; + char _data[1]; + + [[nodiscard]] static size_t SizeWithData(size_t dataLength); + + [[nodiscard]] SSRC ssrc() const; + [[nodiscard]] uint8_t subtype() const; + [[nodiscard]] RtcpAppName name() const; + [[nodiscard]] const char *data() const; + [[nodiscard]] size_t dataSize() const; + + void preparePacket(SSRC ssrc, const RtcpAppName &name, uint8_t subtype, size_t dataLength); + void setSSRC(SSRC ssrc); + void setName(const RtcpAppName &name); + + void log() const; +}; + +struct RTC_CPP_EXPORT RtpRtx { + RtpHeader header; + + [[nodiscard]] const char *getBody() const; + [[nodiscard]] char *getBody(); + [[nodiscard]] size_t getBodySize(size_t totalSize) const; + [[nodiscard]] size_t getSize() const; + [[nodiscard]] uint16_t getOriginalSeqNo() const; + + // Returns the new size of the packet + size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType); + + size_t copyTo(RtpHeader *dest, size_t totalSize, uint8_t originalPayloadType); +}; + +#pragma pack(pop) + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/rtpdepacketizer.hpp b/libdatachannel/include/rtc/rtpdepacketizer.hpp new file mode 100644 index 0000000..0d56819 --- /dev/null +++ b/libdatachannel/include/rtc/rtpdepacketizer.hpp @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2024 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTP_DEPACKETIZER_H +#define RTC_RTP_DEPACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "message.hpp" + +#include + +namespace rtc { + +// Base RTP depacketizer class +class RTC_CPP_EXPORT RtpDepacketizer : public MediaHandler { +public: + RtpDepacketizer(); + RtpDepacketizer(uint32_t clockRate); + virtual ~RtpDepacketizer(); + + virtual void incoming(message_vector &messages, const message_callback &send) override; + +protected: + shared_ptr createFrameInfo(uint32_t timestamp, uint8_t payloadType) const; + +private: + const uint32_t mClockRate; +}; + +// Base class for video RTP depacketizer +class RTC_CPP_EXPORT VideoRtpDepacketizer : public RtpDepacketizer { +public: + inline static const uint32_t ClockRate = 90000; + + VideoRtpDepacketizer(); + virtual ~VideoRtpDepacketizer(); + +protected: + struct sequence_cmp { + bool operator()(message_ptr a, message_ptr b) const; + }; + using message_buffer = std::set; + + virtual message_ptr reassemble(message_buffer &messages) = 0; + +private: + void incoming(message_vector &messages, const message_callback &send) override; + + message_buffer mBuffer; +}; + +// Generic audio RTP depacketizer +template +class RTC_CPP_EXPORT AudioRtpDepacketizer final : public RtpDepacketizer { +public: + inline static const uint32_t DefaultClockRate = DEFAULT_CLOCK_RATE; + + AudioRtpDepacketizer(uint32_t clockRate = DefaultClockRate) : RtpDepacketizer(clockRate) {} +}; + +// Audio RTP depacketizers +using OpusRtpDepacketizer = AudioRtpDepacketizer<48000>; +using AACRtpDepacketizer = AudioRtpDepacketizer<48000>; +using PCMARtpDepacketizer = AudioRtpDepacketizer<8000>; +using PCMURtpDepacketizer = AudioRtpDepacketizer<8000>; +using G722RtpDepacketizer = AudioRtpDepacketizer<8000>; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_RTP_DEPACKETIZER_H */ diff --git a/libdatachannel/include/rtc/rtppacketizationconfig.hpp b/libdatachannel/include/rtc/rtppacketizationconfig.hpp new file mode 100644 index 0000000..238b919 --- /dev/null +++ b/libdatachannel/include/rtc/rtppacketizationconfig.hpp @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTP_PACKETIZATION_CONFIG_H +#define RTC_RTP_PACKETIZATION_CONFIG_H + +#if RTC_ENABLE_MEDIA + +#include "dependencydescriptor.hpp" +#include "rtp.hpp" + +namespace rtc { + +struct RTC_CPP_EXPORT VideoLayersAllocation; + +// RTP configuration used in packetization process +class RTC_CPP_EXPORT RtpPacketizationConfig { +public: + SSRC ssrc; + std::string cname; + uint8_t payloadType; + uint32_t clockRate; + uint8_t videoOrientationId; + + // current sequence number + uint16_t sequenceNumber; + + // current timestamp + uint32_t timestamp; + + // start timestamp + uint32_t startTimestamp; + + /// Current video orientation + /// + /// Bit# 7 6 5 4 3 2 1 0 + /// Definition 0 0 0 0 C F R1 R0 + /// + /// C + /// 0 - Front-facing camera (use this if unsure) + /// 1 - Back-facing camera + /// + /// F + /// 0 - No Flip + /// 1 - Horizontal flip + /// + /// R1 R0 - CW rotation that receiver must apply + /// 0 - 0 degrees + /// 1 - 90 degrees + /// 2 - 180 degrees + /// 3 - 270 degrees + uint8_t videoOrientation = 0; + + // MID Extension Header + uint8_t midId = 0; + optional mid; + + // RID Extension Header + uint8_t ridId = 0; + optional rid; + + // Dependency Descriptor Extension Header + uint8_t dependencyDescriptorId = 0; + + optional dependencyDescriptorContext; + // the negotiated ID of the playout delay header extension + // https://webrtc.googlesource.com/src/+/main/docs/native-code/rtp-hdrext/playout-delay/README.md + uint8_t playoutDelayId = 0; + + // Minimum/maxiumum playout delay, in 10ms intervals. A value of 10 would equal a 100ms delay + uint16_t playoutDelayMin = 0; + uint16_t playoutDelayMax = 0; + + // Google Video Layers Allocation for simulcast + // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-layers-allocation00 + // + // The negotiated extension id + uint8_t videoLayersAllocationId = 0; + // Stream index, unique per RID/SSRC + uint8_t videoLayersAllocationStreamIndex = 0; + // Shared data about layers + std::shared_ptr videoLayersAllocationStreams; + + // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/color-space/ + uint8_t colorSpaceId = 0; // the negotiated ID of color space header extension + uint8_t colorChromaSitingHorz = 0; // unspecified + uint8_t colorChromaSitingVert = 0; // unspecified + uint8_t colorRange = 2; // full range + uint8_t colorPrimaries = 1; // BT.709-6 + uint8_t colorTransfer = 1; // BT.709-6 + uint8_t colorMatrix = 1; // BT.709-6 + + // abs-capture-time RTP header extension. When absCaptureTimeId > 0 and an + // outgoing FrameInfo carries an absCaptureTimeNtp value, the RtpPacketizer + // writes the 8-byte (shortened) form of the abs-capture-time extension into + // every packet for that frame. The receiver can recover the source's + // capture wallclock and compute true glass-to-glass latency. + // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time + uint8_t absCaptureTimeId = 0; + + /// Construct RTP configuration used in packetization process + /// @param ssrc SSRC of source + /// @param cname CNAME of source + /// @param payloadType Payload type of source + /// @param clockRate Clock rate of source used in timestamps + /// nullopt) + /// @param videoOrientationId Video orientation (see above) + RtpPacketizationConfig(SSRC ssrc, std::string cname, uint8_t payloadType, uint32_t clockRate, + uint8_t videoOrientationId = 0); + + RtpPacketizationConfig(const RtpPacketizationConfig &) = delete; + + /// Convert timestamp to seconds + /// @param timestamp Timestamp + /// @param clockRate Clock rate for timestamp calculation + static double getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate); + + /// Convert timestamp to seconds + /// @param timestamp Timestamp + double timestampToSeconds(uint32_t timestamp); + + /// Convert seconds to timestamp + /// @param seconds Number of seconds + /// @param clockRate Clock rate for timestamp calculation + static uint32_t getTimestampFromSeconds(double seconds, uint32_t clockRate); + + /// Convert seconds to timestamp + /// @param seconds Number of seconds + uint32_t secondsToTimestamp(double seconds); +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_RTP_PACKETIZATION_CONFIG_H */ diff --git a/libdatachannel/include/rtc/rtppacketizer.hpp b/libdatachannel/include/rtc/rtppacketizer.hpp new file mode 100644 index 0000000..df3fcc1 --- /dev/null +++ b/libdatachannel/include/rtc/rtppacketizer.hpp @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_RTP_PACKETIZER_H +#define RTC_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "message.hpp" +#include "rtppacketizationconfig.hpp" + +namespace rtc { + +/// RTP packetizer +class RTC_CPP_EXPORT RtpPacketizer : public MediaHandler { +public: + /// Default maximum fragment size (for video packetizers) + inline static const size_t DefaultMaxFragmentSize = RTC_DEFAULT_MAX_FRAGMENT_SIZE; + + /// Clock rate for video in RTP + inline static const uint32_t VideoClockRate = 90 * 1000; + + /// Constructs packetizer with given RTP configuration + /// @note RTP configuration is used in packetization process which may change some configuration + /// properties such as sequence number. + /// @param rtpConfig RTP configuration + RtpPacketizer(shared_ptr rtpConfig); + virtual ~RtpPacketizer(); + + virtual void media(const Description::Media &desc) override; + virtual void outgoing(message_vector &messages, const message_callback &send) override; + + /// RTP packetization config + const shared_ptr rtpConfig; + +protected: + /// Fragment data into payloads + /// Default implementation returns data as a single payload + /// @param data Input data + virtual std::vector fragment(binary data); + + /// Creates an RTP packet for a payload + /// @note This function increases the sequence number. + /// @param payload RTP payload + /// @param mark Set marker flag in RTP packet if true + virtual message_ptr packetize(const binary &payload, bool mark, shared_ptr frameInfo = nullptr); + +private: + static const auto RtpHeaderSize = 12; + static const auto RtpExtHeaderCvoSize = 8; + + uint32_t videoLayersAllocationInitialPacketCount = 0; + + bool shouldEmitVideoLayersAllocation(shared_ptr frameInfo); +}; + +// Generic audio RTP packetizer +template +class RTC_CPP_EXPORT AudioRtpPacketizer final : public RtpPacketizer { +public: + inline static const uint32_t DefaultClockRate = DEFAULT_CLOCK_RATE; + inline static const uint32_t defaultClockRate [[deprecated("Use DefaultClockRate")]] = + DEFAULT_CLOCK_RATE; // for backward compatibility + + AudioRtpPacketizer(shared_ptr rtpConfig) + : RtpPacketizer(std::move(rtpConfig)) {} +}; + +// Audio RTP packetizers +using OpusRtpPacketizer = AudioRtpPacketizer<48000>; +using AACRtpPacketizer = AudioRtpPacketizer<48000>; +using PCMARtpPacketizer = AudioRtpPacketizer<8000>; +using PCMURtpPacketizer = AudioRtpPacketizer<8000>; +using G722RtpPacketizer = AudioRtpPacketizer<8000>; + +// Dummy wrapper for backward compatibility, do not use +class RTC_CPP_EXPORT PacketizationHandler final : public MediaHandler { +public: + PacketizationHandler(shared_ptr packetizer) + : mPacketizer(std::move(packetizer)) {} + + inline void outgoing(message_vector &messages, const message_callback &send) { + return mPacketizer->outgoing(messages, send); + } + +private: + shared_ptr mPacketizer; +}; + +// Audio packetization handlers for backward compatibility, do not use +using OpusPacketizationHandler [[deprecated("Add OpusRtpPacketizer directly")]] = + PacketizationHandler; +using AACPacketizationHandler [[deprecated("Add AACRtpPacketizer directly")]] = + PacketizationHandler; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/track.hpp b/libdatachannel/include/rtc/track.hpp new file mode 100644 index 0000000..f06d78f --- /dev/null +++ b/libdatachannel/include/rtc/track.hpp @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_TRACK_H +#define RTC_TRACK_H + +#include "channel.hpp" +#include "common.hpp" +#include "description.hpp" +#include "mediahandler.hpp" + +#include + +namespace rtc { + +namespace impl { + +class Track; + +} // namespace impl + +class RTC_CPP_EXPORT Track final : private CheshireCat, public Channel { +public: + Track(impl_ptr impl); + ~Track() override; + + string mid() const; + Description::Direction direction() const; + Description::Media description() const; + + void setDescription(Description::Media description); + + void close(void) override; + bool send(message_variant data) override; + bool send(const byte *data, size_t size) override; + + bool isOpen(void) const override; + bool isClosed(void) const override; + size_t maxMessageSize() const override; + + void sendFrame(binary data, FrameInfo info); + void sendFrame(const byte *data, size_t size, FrameInfo info); + void onFrame(std::function callback); + + bool requestKeyframe(SSRC ssrc=0, bool retransmit=false); + bool requestKeyframe(const std::vector& targetSSRCs, bool retransmit=false); + bool requestBitrate(unsigned int bitrate); + bool sendRtcpApp(uint32_t ssrc, const RtcpAppName &name, uint8_t subtype, + const binary &data = binary{}); + + void setMediaHandler(shared_ptr handler); + void chainMediaHandler(shared_ptr handler); + shared_ptr getMediaHandler(); + + // Deprecated, use setMediaHandler() and getMediaHandler() + inline void setRtcpHandler(shared_ptr handler) { setMediaHandler(handler); } + inline shared_ptr getRtcpHandler() { return getMediaHandler(); } + +private: + using CheshireCat::impl; +}; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/utils.hpp b/libdatachannel/include/rtc/utils.hpp new file mode 100644 index 0000000..9ec20b1 --- /dev/null +++ b/libdatachannel/include/rtc/utils.hpp @@ -0,0 +1,174 @@ +/** + * Copyright (c) 2019-2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_UTILS_H +#define RTC_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rtc { + +// overloaded helper +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +// weak_ptr bind helper +template auto weak_bind(F &&f, T *t, Args &&..._args) { + return [bound = std::bind(f, t, _args...), weak_this = t->weak_from_this()](auto &&...args) { + if (auto shared_this = weak_this.lock()) + return bound(args...); + else + return static_cast(false); + }; +} + +// scope_guard helper +class scope_guard final { +public: + scope_guard(std::function func) : function(std::move(func)) {} + scope_guard(scope_guard &&other) = delete; + scope_guard(const scope_guard &) = delete; + void operator=(const scope_guard &) = delete; + + ~scope_guard() { + if (function) + function(); + } + +private: + std::function function; +}; + +// callback with built-in synchronization +template class synchronized_callback { +public: + synchronized_callback() = default; + synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); } + synchronized_callback(const synchronized_callback &cb) { *this = cb; } + synchronized_callback(std::function func) { *this = std::move(func); } + virtual ~synchronized_callback() { *this = nullptr; } + + synchronized_callback &operator=(synchronized_callback &&cb) { + std::scoped_lock lock(mutex, cb.mutex); + set(std::exchange(cb.callback, nullptr)); + return *this; + } + + synchronized_callback &operator=(const synchronized_callback &cb) { + std::scoped_lock lock(mutex, cb.mutex); + set(cb.callback); + return *this; + } + + synchronized_callback &operator=(std::function func) { + std::lock_guard lock(mutex); + set(std::move(func)); + return *this; + } + + bool operator()(Args... args) const { + std::lock_guard lock(mutex); + return call(std::move(args)...); + } + + operator bool() const { + std::lock_guard lock(mutex); + return callback ? true : false; + } + +protected: + virtual void set(std::function func) { callback = std::move(func); } + virtual bool call(Args... args) const { + if (!callback) + return false; + + callback(std::move(args)...); + return true; + } + + std::function callback; + mutable std::recursive_mutex mutex; +}; + +// callback with built-in synchronization and replay of the last missed call +template +class synchronized_stored_callback final : public synchronized_callback { +public: + template + synchronized_stored_callback(CArgs &&...cargs) + : synchronized_callback(std::forward(cargs)...) {} + ~synchronized_stored_callback() {} + +private: + void set(std::function func) { + synchronized_callback::set(func); + if (func && stored) { + std::apply(func, std::move(*stored)); + stored.reset(); + } + } + + bool call(Args... args) const { + if (!synchronized_callback::call(args...)) + stored.emplace(std::move(args)...); + + return true; + } + + mutable std::optional> stored; +}; + +// pimpl base class +template using impl_ptr = std::shared_ptr; +template class CheshireCat { +public: + CheshireCat(impl_ptr impl) : mImpl(std::move(impl)) {} + template + CheshireCat(Args... args) : mImpl(std::make_shared(std::forward(args)...)) {} + CheshireCat(CheshireCat &&cc) { *this = std::move(cc); } + CheshireCat(const CheshireCat &) = delete; + + virtual ~CheshireCat() = default; + + CheshireCat &operator=(CheshireCat &&cc) { + mImpl = std::move(cc.mImpl); + return *this; + }; + CheshireCat &operator=(const CheshireCat &) = delete; + +protected: + impl_ptr impl() { return mImpl; } + impl_ptr impl() const { return mImpl; } + +private: + impl_ptr mImpl; +}; + +// case-insensitive string comparator using std::tolower +class case_insensitive_less : public std::less { +public: + bool operator()(const std::string &lhs, const std::string &rhs) const { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), + [](auto a, auto b) { + return std::tolower(static_cast(a)) < + std::tolower(static_cast(b)); + }); + } +}; + +} // namespace rtc + +#endif diff --git a/libdatachannel/include/rtc/version.h b/libdatachannel/include/rtc/version.h new file mode 100644 index 0000000..1c339bf --- /dev/null +++ b/libdatachannel/include/rtc/version.h @@ -0,0 +1,9 @@ +#ifndef RTC_VERSION_H +#define RTC_VERSION_H + +#define RTC_VERSION_MAJOR 0 +#define RTC_VERSION_MINOR 24 +#define RTC_VERSION_PATCH 4 +#define RTC_VERSION "0.24.4" + +#endif diff --git a/libdatachannel/include/rtc/video_layers_allocation.hpp b/libdatachannel/include/rtc/video_layers_allocation.hpp new file mode 100644 index 0000000..31590bc --- /dev/null +++ b/libdatachannel/include/rtc/video_layers_allocation.hpp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2026 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_VIDEO_LAYERS_ALLOCATION_H +#define RTC_VIDEO_LAYERS_ALLOCATION_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" + +#include +#include +#include + +namespace rtc { + +// Google Video Layers Allocation for simulcast +// +// https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-layers-allocation00 + +struct RTC_CPP_EXPORT VideoLayersAllocation { + struct SpatialLayer { + uint16_t width = 0; + uint16_t height = 0; + uint8_t fps = 0; + std::vector targetBitratesKbps; // per temporal layer, cumulative, cannot be empty + }; + + struct RtpStream { + std::vector spatialLayers; + }; + + std::vector rtpStreams; // up to 4 streams + + /// Generate the wire format for Google Video Layers Allocation RTP header extension + /// @param streamIndex The RTP stream index (0-3) for this packet's stream + /// @return Binary payload for the RTP header extension, empty if allocation is invalid + binary generate(uint8_t streamIndex) const; +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_VIDEO_LAYERS_ALLOCATION_H */ diff --git a/libdatachannel/include/rtc/vp8rtpdepacketizer.hpp b/libdatachannel/include/rtc/vp8rtpdepacketizer.hpp new file mode 100644 index 0000000..beeea4c --- /dev/null +++ b/libdatachannel/include/rtc/vp8rtpdepacketizer.hpp @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_VP8_RTP_DEPACKETIZER_H +#define RTC_VP8_RTP_DEPACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "message.hpp" +#include "rtpdepacketizer.hpp" + +namespace rtc { + +/// RTP depacketization for VP8 +class RTC_CPP_EXPORT VP8RtpDepacketizer final : public VideoRtpDepacketizer { +public: + VP8RtpDepacketizer(); + ~VP8RtpDepacketizer(); + +private: + message_ptr reassemble(message_buffer &buffer) override; +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif /* RTC_VP8_RTP_DEPACKETIZER_H */ diff --git a/libdatachannel/include/rtc/vp8rtppacketizer.hpp b/libdatachannel/include/rtc/vp8rtppacketizer.hpp new file mode 100644 index 0000000..a8f121a --- /dev/null +++ b/libdatachannel/include/rtc/vp8rtppacketizer.hpp @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2026 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_VP8_RTP_PACKETIZER_H +#define RTC_VP8_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "rtppacketizer.hpp" + +namespace rtc { + +/// RTP packetization for VP8 +class RTC_CPP_EXPORT VP8RtpPacketizer final : public RtpPacketizer { +public: + inline static const uint32_t ClockRate = VideoClockRate; + [[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate; + + /// Constructs VP8 payload packetizer with given RTP configuration. + /// @note RTP configuration is used in packetization process which may change some configuration + /// properties such as sequence number. + /// @param rtpConfig RTP configuration + /// @param maxFragmentSize maximum size of one packet payload + VP8RtpPacketizer(shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + +private: + std::vector fragment(binary frame) override; + + const size_t mMaxFragmentSize; +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_VP8_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/vp9rtpdepacketizer.hpp b/libdatachannel/include/rtc/vp9rtpdepacketizer.hpp new file mode 100644 index 0000000..853936a --- /dev/null +++ b/libdatachannel/include/rtc/vp9rtpdepacketizer.hpp @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_VP9_RTP_DEPACKETIZER_H +#define RTC_VP9_RTP_DEPACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "common.hpp" +#include "message.hpp" +#include "rtpdepacketizer.hpp" + +namespace rtc { + +/// RTP depacketization for VP9 +class RTC_CPP_EXPORT VP9RtpDepacketizer final : public VideoRtpDepacketizer { +public: + VP9RtpDepacketizer(); + ~VP9RtpDepacketizer(); + +private: + message_ptr reassemble(message_buffer &buffer) override; +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif /* RTC_VP9_RTP_DEPACKETIZER_H */ diff --git a/libdatachannel/include/rtc/vp9rtppacketizer.hpp b/libdatachannel/include/rtc/vp9rtppacketizer.hpp new file mode 100644 index 0000000..85637f2 --- /dev/null +++ b/libdatachannel/include/rtc/vp9rtppacketizer.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2026 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_VP9_RTP_PACKETIZER_H +#define RTC_VP9_RTP_PACKETIZER_H + +#if RTC_ENABLE_MEDIA + +#include "rtppacketizer.hpp" + +namespace rtc { + +/// RTP packetization for VP9 +class RTC_CPP_EXPORT VP9RtpPacketizer final : public RtpPacketizer { +public: + inline static const uint32_t ClockRate = VideoClockRate; + + /// Constructs VP9 payload packetizer with given RTP configuration. + /// @note RTP configuration is used in packetization process which may change some configuration + /// properties such as sequence number. + /// @param rtpConfig RTP configuration + /// @param maxFragmentSize maximum size of one packet payload + VP9RtpPacketizer(shared_ptr rtpConfig, + size_t maxFragmentSize = DefaultMaxFragmentSize); + +private: + std::vector fragment(binary frame) override; + + const size_t mMaxFragmentSize; +}; + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ + +#endif /* RTC_VP9_RTP_PACKETIZER_H */ diff --git a/libdatachannel/include/rtc/websocket.hpp b/libdatachannel/include/rtc/websocket.hpp new file mode 100644 index 0000000..cbb72e6 --- /dev/null +++ b/libdatachannel/include/rtc/websocket.hpp @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2020-2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_WEBSOCKET_H +#define RTC_WEBSOCKET_H + +#if RTC_ENABLE_WEBSOCKET + +#include "channel.hpp" +#include "common.hpp" +#include "configuration.hpp" + +#include + +namespace rtc { + +namespace impl { + +struct WebSocket; + +} // namespace impl + +class RTC_CPP_EXPORT WebSocket final : private CheshireCat, public Channel { +public: + enum class State : int { + Connecting = 0, + Open = 1, + Closing = 2, + Closed = 3, + }; + + using Configuration = WebSocketConfiguration; + + using Headers = std::map; + + WebSocket(); + WebSocket(Configuration config); + WebSocket(impl_ptr impl); + ~WebSocket() override; + + State readyState() const; + + bool isOpen() const override; + bool isClosed() const override; + size_t maxMessageSize() const override; + + void open(const string &url, const Headers &headers = {}); + void close() override; + void forceClose(); + bool send(const message_variant data) override; + bool send(const byte *data, size_t size) override; + + optional remoteAddress() const; + optional path() const; + std::multimap requestHeaders() const; + +private: + using CheshireCat::impl; +}; + +std::ostream &operator<<(std::ostream &out, WebSocket::State state); + +} // namespace rtc + +#endif + +#endif // RTC_WEBSOCKET_H diff --git a/libdatachannel/include/rtc/websocketserver.hpp b/libdatachannel/include/rtc/websocketserver.hpp new file mode 100644 index 0000000..9bb1c0d --- /dev/null +++ b/libdatachannel/include/rtc/websocketserver.hpp @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2021 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_WEBSOCKETSERVER_H +#define RTC_WEBSOCKETSERVER_H + +#if RTC_ENABLE_WEBSOCKET + +#include "common.hpp" +#include "configuration.hpp" +#include "websocket.hpp" + +namespace rtc { + +namespace impl { + +struct WebSocketServer; + +} + +class RTC_CPP_EXPORT WebSocketServer final : private CheshireCat { +public: + using Configuration = WebSocketServerConfiguration; + + WebSocketServer(); + WebSocketServer(Configuration config); + ~WebSocketServer(); + + void stop(); + + uint16_t port() const; + + void onClient(std::function)> callback); + +private: + using CheshireCat::impl; +}; + +} // namespace rtc + +#endif + +#endif // RTC_WEBSOCKET_H diff --git a/libdatachannel/lib/MbedTLS.lib b/libdatachannel/lib/MbedTLS.lib new file mode 100644 index 0000000..ec317b4 Binary files /dev/null and b/libdatachannel/lib/MbedTLS.lib differ diff --git a/libdatachannel/lib/datachannel-static.lib b/libdatachannel/lib/datachannel-static.lib new file mode 100644 index 0000000..d294505 Binary files /dev/null and b/libdatachannel/lib/datachannel-static.lib differ diff --git a/libdatachannel/lib/juice-static.lib b/libdatachannel/lib/juice-static.lib new file mode 100644 index 0000000..5aaba49 Binary files /dev/null and b/libdatachannel/lib/juice-static.lib differ diff --git a/libdatachannel/lib/usrsctp.lib b/libdatachannel/lib/usrsctp.lib new file mode 100644 index 0000000..733ccbb Binary files /dev/null and b/libdatachannel/lib/usrsctp.lib differ