Compare commits

...

46 Commits

Author SHA1 Message Date
495b389e05 Fixed signaling state callback in C API test 2020-11-21 18:05:43 +01:00
e83494df09 Bumped version to 0.10.3 2020-11-21 17:28:24 +01:00
7bf87c6989 Updated libjuice to 0.6.1 2020-11-21 17:27:51 +01:00
cb591de15f Added DiffServ QoS in features 2020-11-21 16:06:09 +01:00
eb4540e319 Merge pull request #271 from paullouisageneau/fix-dscp-rtcp
Send RTCP with the same DSCP as RTP
2020-11-21 16:04:44 +01:00
5d34439cb7 Added warning when track queue is full 2020-11-21 15:55:51 +01:00
b19e9077af Reformatting 2020-11-21 15:52:58 +01:00
4ff010b20b Added maybe_unused 2020-11-21 15:51:49 +01:00
442e50825c Changed warnings for media support 2020-11-21 15:47:51 +01:00
9f2801b7b9 Set the same DSCP for RTP and RTCP 2020-11-21 15:46:54 +01:00
3b0bf3a152 Updated libjuice 2020-11-21 15:30:14 +01:00
fe4a9ec453 Merge pull request #269 from paullouisageneau/update-libsrtp
Update libsrtp
2020-11-21 13:17:45 +01:00
dc1d14adf1 Merge pull request #270 from paullouisageneau/fix-dscp
Fix DSCP for tracks
2020-11-21 13:17:31 +01:00
14918c16e8 Fixed track DSCP 2020-11-21 12:43:34 +01:00
a023acfa33 Updated libsrtp 2020-11-21 12:43:00 +01:00
f098019c1f Merge pull request #268 from paullouisageneau/dscp
Differentiated Services support
2020-11-21 00:56:05 +01:00
a67ca9da9b Implemented support for DSCP 2020-11-21 00:45:18 +01:00
613ebf2564 Merge pull request #267 from paullouisageneau/windows-examples-dynamic
Link examples against dynamic library on Windows
2020-11-20 00:46:36 +01:00
dc6427770c Made winsock library dependency public 2020-11-20 00:29:39 +01:00
81e0a05a1a Added missing exports 2020-11-20 00:29:31 +01:00
9ea613f05f Also link tests against the dynamic library on Windows 2020-11-20 00:13:56 +01:00
eb4a764648 Disable MSVC warning 4251 in header rather than in CMakeLists 2020-11-20 00:04:53 +01:00
baf8a3adce Made examples also link against the dynamic library on Windows 2020-11-19 23:39:49 +01:00
d9aec59352 Fixed include 2020-11-19 23:29:44 +01:00
3ff5801512 Merge pull request #264 from hanseuljun/cpp-export
Exporting C++ API for Windows
2020-11-19 23:02:34 +01:00
fcc2577e11 Merge pull request #266 from supercairos/fix/msvc-w4-build-error-shadow-class-member
fix build warning on windows when built with MSVC /W4.
2020-11-19 23:00:10 +01:00
b4865f26e4 Move RTC_CPP_EXPORT from log.hpp to include.hpp, remove RTC_CPP_EXPORT from /src classes, and add RTC_CPP_EXPORT to Description. 2020-11-19 08:09:00 -08:00
fc6d5afdd9 fix build warning on windows when built with MSVC /W4.
Some arguments where conflicting with data member names.
2020-11-19 14:10:52 +01:00
7a49c0b88b Updated libjuice 2020-11-19 09:41:29 +01:00
679c0ccd2e Add more dllexports, leave RTC_CPP_EXPORT only in log.hpp, and ignore C4251 warning. 2020-11-18 14:55:17 -08:00
ee3bc9694b Bumped version to 0.10.2 2020-11-18 23:39:06 +01:00
0c0ba77de5 Merge pull request #265 from paullouisageneau/optimize-sctp-recv
Schedule SCTP recv task only if necessary
2020-11-18 23:38:49 +01:00
8729e0d2aa Scheduled SCTP recv task only if there is no pending task 2020-11-18 23:26:47 +01:00
12098e7c41 Start exporting c++ API for windows. 2020-11-17 22:20:54 -08:00
90eb610bfe Merge pull request #263 from paullouisageneau/release-rdesc-lock
Prevent holding multiple locks
2020-11-17 23:02:15 +01:00
08ddfa1276 Release remote description lock before passing candidate to transport 2020-11-17 22:41:42 +01:00
87df64a002 Merge pull request #262 from paullouisageneau/fix-duplicate-candidates
Prevent duplicate candidates
2020-11-17 20:40:15 +01:00
5af414d0df Cosmetic fixes in Description 2020-11-17 20:23:59 +01:00
2443c72350 Refactored trimming with util functions 2020-11-17 20:10:47 +01:00
f033e4ab8f Prevent whitspaces at the end of candidates as they confuse libnice 2020-11-17 19:57:51 +01:00
1a6dcdce6f Reordered Candidate getters 2020-11-17 19:28:13 +01:00
100039eba8 Enforce candidates uniqueness in description 2020-11-17 19:23:29 +01:00
e2005c789a Refactored candidate storage and split parsing and resolution 2020-11-17 19:21:48 +01:00
819566b4c1 Merge pull request #261 from paullouisageneau/fix-remote-unordered-flag
Fix remote unordered flag
2020-11-17 00:15:35 +01:00
82caab8906 Added tests for remote protocol and reliability in C API 2020-11-16 23:59:59 +01:00
802516b2db Fixed remote DataChannel unordered flag 2020-11-16 23:59:59 +01:00
37 changed files with 437 additions and 318 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7)
project(libdatachannel
VERSION 0.10.1
VERSION 0.10.3
LANGUAGES CXX)
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
@ -36,7 +36,7 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
if (MSVC)
if(MSVC)
add_definitions(-DNOMINMAX)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
@ -163,8 +163,8 @@ target_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog)
target_link_libraries(datachannel-static PRIVATE Usrsctp::Usrsctp)
if(WIN32)
target_link_libraries(datachannel PRIVATE ws2_32) # winsock2
target_link_libraries(datachannel-static PRIVATE ws2_32) # winsock2
target_link_libraries(datachannel PUBLIC ws2_32) # winsock2
target_link_libraries(datachannel-static PUBLIC ws2_32) # winsock2
endif()
if(NO_MEDIA)
@ -272,11 +272,7 @@ if(NOT NO_TESTS)
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
endif()
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(WIN32)
target_link_libraries(datachannel-tests datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-tests datachannel)
endif()
target_link_libraries(datachannel-tests datachannel)
# Benchmark
add_executable(datachannel-benchmark test/benchmark.cpp)
@ -288,11 +284,7 @@ if(NOT NO_TESTS)
endif()
target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(WIN32)
target_link_libraries(datachannel-benchmark datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-benchmark datachannel)
endif()
target_link_libraries(datachannel-benchmark datachannel)
endif()
# Examples

View File

@ -27,8 +27,9 @@ Features:
- Full IPv6 support
- Trickle ICE ([draft-ietf-ice-trickle-21](https://tools.ietf.org/html/draft-ietf-ice-trickle-21))
- JSEP compatible ([draft-ietf-rtcweb-jsep-26](https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26))
- SRTP and SRTCP key derivation from DTLS ([RFC5764](https://tools.ietf.org/html/rfc5764))
- Multicast DNS candidates ([draft-ietf-rtcweb-mdns-ice-candidates-04](https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-04))
- SRTP and SRTCP key derivation from DTLS ([RFC5764](https://tools.ietf.org/html/rfc5764))
- Differentiated Services QoS ([draft-ietf-tsvwg-rtcweb-qos-18](https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18))
- TURN relaying ([RFC5766](https://tools.ietf.org/html/rfc5766)) with [libnice](https://github.com/libnice/libnice) as ICE backend
Note only SDP BUNDLE mode is supported for media multiplexing ([draft-ietf-mmusic-sdp-bundle-negotiation-54](https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54)). The behavior is equivalent to the JSEP bundle-only policy: the library always negociates one unique network component, where SRTP media streams are multiplexed with SRTCP control packets ([RFC5761](https://tools.ietf.org/html/rfc5761)) and SCTP/DTLS data traffic ([RFC5764](https://tools.ietf.org/html/rfc5764)).

2
deps/libjuice vendored

2
deps/libsrtp vendored

View File

@ -9,14 +9,9 @@ target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
else()
add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
endif()
set_target_properties(datachannel-client PROPERTIES
CXX_STANDARD 17
OUTPUT_NAME client)
if(WIN32)
target_link_libraries(datachannel-client datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-client datachannel)
endif()
target_link_libraries(datachannel-client datachannel nlohmann_json)

View File

@ -4,19 +4,11 @@ add_executable(datachannel-copy-paste-offerer offerer.cpp)
set_target_properties(datachannel-copy-paste-offerer PROPERTIES
CXX_STANDARD 17
OUTPUT_NAME offerer)
if(WIN32)
target_link_libraries(datachannel-copy-paste-offerer datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-copy-paste-offerer datachannel)
endif()
target_link_libraries(datachannel-copy-paste-offerer datachannel)
add_executable(datachannel-copy-paste-answerer answerer.cpp)
set_target_properties(datachannel-copy-paste-answerer PROPERTIES
CXX_STANDARD 17
OUTPUT_NAME answerer)
if(WIN32)
target_link_libraries(datachannel-copy-paste-answerer datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-copy-paste-answerer datachannel)
endif()
target_link_libraries(datachannel-copy-paste-answerer datachannel)

View File

@ -4,11 +4,5 @@ add_executable(datachannel-media main.cpp)
set_target_properties(datachannel-media PROPERTIES
CXX_STANDARD 17
OUTPUT_NAME media)
target_link_libraries(datachannel-media datachannel nlohmann_json)
if(WIN32)
target_link_libraries(datachannel-media datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-media datachannel)
endif()
target_link_libraries(datachannel-media datachannel nlohmann_json)

View File

@ -4,12 +4,5 @@ add_executable(datachannel-sfu-media main.cpp)
set_target_properties(datachannel-sfu-media PROPERTIES
CXX_STANDARD 17
OUTPUT_NAME sfu-media)
if(WIN32)
target_link_libraries(datachannel-sfu-media datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-sfu-media datachannel)
endif()
target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)

View File

@ -25,7 +25,7 @@
namespace rtc {
class Candidate {
class RTC_CPP_EXPORT Candidate {
public:
enum class Family { Unresolved, Ipv4, Ipv6 };
enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
@ -40,35 +40,44 @@ public:
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;
Type type() const;
TransportType transportType() const;
std::optional<string> address() const;
std::optional<uint16_t> port() const;
std::optional<uint32_t> priority() const;
private:
string mCandidate;
void parse(string candidate);
string mFoundation;
uint32_t mComponent, mPriority;
string mTypeString, mTransportString;
Type mType;
TransportType mTransportType;
string mNode, mService;
string mTail;
std::optional<string> mMid;
// Extracted on resolution
Family mFamily;
Type mType;
TransportType mTransportType;
string mAddress;
uint16_t mPort;
uint32_t mPriority;
};
} // namespace rtc
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
#endif

View File

@ -28,7 +28,7 @@
namespace rtc {
class Channel {
class RTC_CPP_EXPORT Channel {
public:
Channel() = default;
virtual ~Channel() = default;

View File

@ -26,7 +26,7 @@
namespace rtc {
struct IceServer {
struct RTC_CPP_EXPORT IceServer {
enum class Type { Stun, Turn };
enum class RelayType { TurnUdp, TurnTcp, TurnTls };
@ -51,7 +51,7 @@ struct IceServer {
RelayType relayType;
};
struct ProxyServer {
struct RTC_CPP_EXPORT ProxyServer {
enum class Type { None = 0, Socks5, Http, Last = Http };
ProxyServer(Type type_, string ip_, uint16_t port_, string username_ = "",
@ -64,7 +64,7 @@ struct ProxyServer {
string password;
};
struct Configuration {
struct RTC_CPP_EXPORT Configuration {
std::vector<IceServer> iceServers;
std::optional<ProxyServer> proxyServer;
bool enableIceTcp = false;

View File

@ -36,7 +36,7 @@ namespace rtc {
class SctpTransport;
class PeerConnection;
class DataChannel : public std::enable_shared_from_this<DataChannel>, public Channel {
class RTC_CPP_EXPORT DataChannel : public std::enable_shared_from_this<DataChannel>, public Channel {
public:
DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
Reliability reliability);
@ -87,7 +87,7 @@ private:
friend class PeerConnection;
};
class NegociatedDataChannel final : public DataChannel {
class RTC_CPP_EXPORT NegociatedDataChannel final : public DataChannel {
public:
NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
string protocol, Reliability reliability);

View File

@ -32,7 +32,7 @@
namespace rtc {
class Description {
class RTC_CPP_EXPORT Description {
public:
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
enum class Role { ActPass, Passive, Active };
@ -53,6 +53,7 @@ public:
void hintType(Type type);
void setFingerprint(string fingerprint);
bool hasCandidate(const Candidate &candidate) const;
void addCandidate(Candidate candidate);
void addCandidates(std::vector<Candidate> candidates);
void endCandidates();
@ -62,7 +63,7 @@ public:
string generateSdp(string_view eol) const;
string generateApplicationSdp(string_view eol) const;
class Entry {
class RTC_CPP_EXPORT Entry {
public:
virtual ~Entry() = default;
@ -94,7 +95,7 @@ public:
Direction mDirection;
};
struct Application : public Entry {
struct RTC_CPP_EXPORT Application : public Entry {
public:
Application(string mid = "data");
virtual ~Application() = default;
@ -119,7 +120,7 @@ public:
};
// Media (non-data)
class Media : public Entry {
class RTC_CPP_EXPORT Media : public Entry {
public:
Media(const string &sdp);
Media(const string &mline, string mid, Direction dir = Direction::SendOnly);
@ -182,7 +183,7 @@ public:
void addRTPMap(const RTPMap &map);
};
class Audio : public Media {
class RTC_CPP_EXPORT Audio : public Media {
public:
Audio(string mid = "audio", Direction dir = Direction::SendOnly);
@ -190,7 +191,7 @@ public:
void addOpusCodec(int payloadType);
};
class Video : public Media {
class RTC_CPP_EXPORT Video : public Media {
public:
Video(string mid = "video", Direction dir = Direction::SendOnly);
@ -244,8 +245,8 @@ private:
} // namespace rtc
std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
#endif

View File

@ -28,9 +28,15 @@
#endif
#ifdef _WIN32
#define RTC_CPP_EXPORT __declspec(dllexport)
#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
#else
#define RTC_CPP_EXPORT
#endif
#include "log.hpp"

View File

@ -27,7 +27,7 @@ namespace rtc {
using init_token = std::shared_ptr<void>;
class Init {
class RTC_CPP_EXPORT Init {
public:
static init_token Token();
static void Preload();

View File

@ -35,6 +35,8 @@
#pragma warning(pop)
#endif
#include "include.hpp"
namespace rtc {
enum class LogLevel { // Don't change, it must match plog severity
@ -47,8 +49,8 @@ enum class LogLevel { // Don't change, it must match plog severity
Verbose = 6
};
void InitLogger(LogLevel level);
void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
RTC_CPP_EXPORT void InitLogger(LogLevel level);
RTC_CPP_EXPORT void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
} // namespace rtc
#endif

View File

@ -29,7 +29,7 @@
namespace rtc {
struct Message : binary {
struct RTC_CPP_EXPORT Message : binary {
enum Type { Binary, String, Control, Reset };
Message(const Message &message) = default;
@ -42,7 +42,8 @@ struct Message : binary {
Message(binary &&data, Type type_ = Binary) : binary(std::move(data)), type(type_) {}
Type type;
unsigned int stream = 0;
unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
unsigned int dscp = 0; // Differentiated Services Code Point
std::shared_ptr<Reliability> reliability;
};

View File

@ -50,14 +50,14 @@ class SctpTransport;
using certificate_ptr = std::shared_ptr<Certificate>;
using future_certificate_ptr = std::shared_future<certificate_ptr>;
struct DataChannelInit {
struct RTC_CPP_EXPORT DataChannelInit {
Reliability reliability = {};
bool negotiated = false;
std::optional<uint16_t> id = nullopt;
string protocol = "";
};
class PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
class RTC_CPP_EXPORT PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
public:
enum class State : int {
New = RTC_NEW,
@ -203,8 +203,8 @@ private:
} // namespace rtc
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state);
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::SignalingState state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::SignalingState state);
#endif

View File

@ -29,7 +29,7 @@
namespace rtc {
class RtcpHandler {
class RTC_CPP_EXPORT RtcpHandler {
protected:
/**
* Use this callback when trying to send custom data (such as RTCP) to the client.
@ -64,7 +64,7 @@ public:
class Track;
// An RtcpSession can be plugged into a Track to handle the whole RTCP session
class RtcpReceivingSession : public RtcpHandler {
class RTC_CPP_EXPORT RtcpReceivingSession : public RtcpHandler {
public:
rtc::message_ptr incoming(rtc::message_ptr ptr) override;
rtc::message_ptr outgoing(rtc::message_ptr ptr) override;

View File

@ -20,7 +20,7 @@
#ifndef RTC_RTP_HPP
#define RTC_RTP_HPP
#include <rtc/log.hpp>
#include "log.hpp"
#include <cmath>
@ -75,19 +75,15 @@ public:
inline void setPayloadType(uint8_t newPayloadType) {
_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
}
inline void setSsrc(uint32_t ssrc) { _ssrc = htonl(ssrc); }
inline void setSsrc(uint32_t in_ssrc) { _ssrc = htonl(in_ssrc); }
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
void log() {
PLOG_VERBOSE << "RTP V: " << (int) version()
<< " P: " << (padding() ? "P" : " ")
<< " X: " << (extension() ? "X" : " ")
<< " CC: " << (int) csrcCount()
<< " M: " << (marker() ? "M" : " ")
<< " PT: " << (int) payloadType()
<< " SEQNO: " << seqNumber()
<< " TS: " << timestamp();
PLOG_VERBOSE << "RTP V: " << (int)version() << " P: " << (padding() ? "P" : " ")
<< " X: " << (extension() ? "X" : " ") << " CC: " << (int)csrcCount()
<< " M: " << (marker() ? "M" : " ") << " PT: " << (int)payloadType()
<< " SEQNO: " << seqNumber() << " TS: " << timestamp();
}
};
@ -103,13 +99,13 @@ private:
uint32_t _delaySinceLastReport;
public:
inline void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost,
inline void preparePacket(SSRC in_ssrc, [[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
uint64_t lastSR_DELAY) {
setSeqNo(highestSeqNo, seqNoCycles);
setJitter(jitter);
setSSRC(ssrc);
setSSRC(in_ssrc);
// Middle 32 bits of NTP Timestamp
// this->lastReport = lastSR_NTP >> 16u;
@ -120,7 +116,7 @@ public:
// this->delaySinceLastReport = lastSR_DELAY;
}
inline void setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); }
inline void setSSRC(SSRC in_ssrc) { this->ssrc = htonl(in_ssrc); }
inline SSRC getSSRC() const { return ntohl(ssrc); }
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
@ -199,9 +195,9 @@ public:
inline void log() const {
PLOG_VERBOSE << "RTCP header: "
<< "version=" << unsigned(version()) << ", padding=" << padding()
<< ", reportCount=" << unsigned(reportCount())
<< ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
<< "version=" << unsigned(version()) << ", padding=" << padding()
<< ", reportCount=" << unsigned(reportCount())
<< ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
}
};
@ -337,7 +333,7 @@ struct RTCP_REMB {
return sizeof(uint32_t) * (1 + header.header.length());
}
void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate) {
// Report Count becomes the format here.
header.header.prepareHeader(206, 15, 0);
@ -352,21 +348,21 @@ struct RTCP_REMB {
id[2] = 'M';
id[3] = 'B';
setBitrate(numSSRC, bitrate);
setBitrate(numSSRC, in_bitrate);
}
void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
void setBitrate(unsigned int numSSRC, unsigned int in_bitrate) {
unsigned int exp = 0;
while (bitrate > pow(2, 18) - 1) {
while (in_bitrate > pow(2, 18) - 1) {
exp++;
bitrate /= 2;
in_bitrate /= 2;
}
// "length" in packet is one less than the number of 32 bit words in the packet.
header.header.setLength(
uint16_t((offsetof(RTCP_REMB, ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate);
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | in_bitrate);
}
void setSsrc(int iterator, SSRC newSssrc) { ssrc[iterator] = htonl(newSssrc); }
@ -428,7 +424,7 @@ public:
public:
void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
header.header.prepareHeader(205, 1, 2 + discreteSeqNoCount);
header.header.prepareHeader(205, 1, 2 + uint16_t(discreteSeqNoCount));
header.setMediaSourceSSRC(ssrc);
header.setPacketSenderSSRC(ssrc);
}

View File

@ -35,7 +35,7 @@ namespace rtc {
class DtlsSrtpTransport;
#endif
class Track final : public std::enable_shared_from_this<Track>, public Channel {
class RTC_CPP_EXPORT Track final : public std::enable_shared_from_this<Track>, public Channel {
public:
Track(Description::Media description);
~Track() = default;
@ -70,8 +70,8 @@ private:
std::weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
#endif
bool outgoing(message_ptr message);
void incoming(message_ptr message);
bool outgoing(message_ptr message);
Description::Media mMediaDescription;
std::atomic<bool> mIsClosed = false;

View File

@ -38,7 +38,7 @@ class TcpTransport;
class TlsTransport;
class WsTransport;
class WebSocket final : public Channel, public std::enable_shared_from_this<WebSocket> {
class RTC_CPP_EXPORT WebSocket final : public Channel, public std::enable_shared_from_this<WebSocket> {
public:
enum class State : int {
Connecting = 0,

View File

@ -20,6 +20,7 @@
#include <algorithm>
#include <array>
#include <cctype>
#include <sstream>
#include <unordered_map>
@ -39,39 +40,44 @@ using std::string;
namespace {
inline bool hasprefix(const string &str, const string &prefix) {
inline bool match_prefix(const string &str, const string &prefix) {
return str.size() >= prefix.size() &&
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
}
inline void trim_begin(string &str) {
str.erase(str.begin(),
std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); }));
}
inline void trim_end(string &str) {
str.erase(
std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
str.end());
}
} // namespace
namespace rtc {
Candidate::Candidate()
: mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
mPort(0), mPriority(0) {}
: mFoundation("none"), mComponent(0), mPriority(0), mTypeString("unknown"),
mTransportString("unknown"), mType(Type::Unknown), mTransportType(TransportType::Unknown),
mNode("0.0.0.0"), mService("9"), mFamily(Family::Unresolved), mPort(0) {}
Candidate::Candidate(string candidate) : Candidate() {
const std::array prefixes{"a=", "candidate:"};
for (const string &prefix : prefixes)
if (hasprefix(candidate, prefix))
candidate.erase(0, prefix.size());
mCandidate = std::move(candidate);
if (!candidate.empty())
parse(std::move(candidate));
}
Candidate::Candidate(string candidate, string mid) : Candidate(std::move(candidate)) {
Candidate::Candidate(string candidate, string mid) : Candidate() {
if (!candidate.empty())
parse(std::move(candidate));
if (!mid.empty())
mMid.emplace(std::move(mid));
}
void Candidate::hintMid(string mid) {
if (!mMid)
mMid.emplace(std::move(mid));
}
bool Candidate::resolve(ResolveMode mode) {
void Candidate::parse(string candidate) {
using TypeMap_t = std::unordered_map<string, Type>;
using TcpTypeMap_t = std::unordered_map<string, TransportType>;
@ -84,99 +90,122 @@ bool Candidate::resolve(ResolveMode mode) {
{"passive", TransportType::TcpPassive},
{"so", TransportType::TcpSo}};
if (mFamily != Family::Unresolved)
return true;
const std::array prefixes{"a=", "candidate:"};
for (const string &prefix : prefixes)
if (match_prefix(candidate, prefix))
candidate.erase(0, prefix.size());
if (mCandidate.empty())
throw std::logic_error("Candidate is empty");
PLOG_VERBOSE << "Resolving candidate (mode="
<< (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mCandidate;
PLOG_VERBOSE << "Parsing candidate: " << candidate;
// See RFC 8445 for format
std::istringstream iss(mCandidate);
int component{0}, priority{0};
string foundation, transport, node, service, typ_, type;
if (iss >> foundation >> component >> transport >> priority &&
iss >> node >> service >> typ_ >> type && typ_ == "typ") {
std::istringstream iss(candidate);
string transport, typ_, type;
if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
iss >> mNode >> mService >> typ_ >> mTypeString && typ_ == "typ"))
throw std::invalid_argument("Invalid candidate format");
string left;
std::getline(iss, left);
std::getline(iss, mTail);
trim_begin(mTail);
trim_end(mTail);
if (auto it = TypeMap.find(type); it != TypeMap.end())
mType = it->second;
else
mType = Type::Unknown;
if (auto it = TypeMap.find(type); it != TypeMap.end())
mType = it->second;
else
mType = Type::Unknown;
if (transport == "UDP" || transport == "udp") {
mTransportType = TransportType::Udp;
} else if (transport == "TCP" || transport == "tcp") {
std::istringstream iss(left);
string tcptype_, tcptype;
if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
mTransportType = it->second;
else
mTransportType = TransportType::TcpUnknown;
} else {
if (transport == "UDP" || transport == "udp") {
mTransportType = TransportType::Udp;
} else if (transport == "TCP" || transport == "tcp") {
// Peek tail to find TCP type
std::istringstream iss(mTail);
string tcptype_, tcptype;
if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
mTransportType = it->second;
else
mTransportType = TransportType::TcpUnknown;
}
} else {
mTransportType = TransportType::Unknown;
mTransportType = TransportType::TcpUnknown;
}
} else {
mTransportType = TransportType::Unknown;
}
}
// Try to resolve the node
struct addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
if (mTransportType == TransportType::Udp) {
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
} else if (mTransportType != TransportType::Unknown) {
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
}
void Candidate::hintMid(string mid) {
if (!mMid)
mMid.emplace(std::move(mid));
}
if (mode == ResolveMode::Simple)
hints.ai_flags |= AI_NUMERICHOST;
bool Candidate::resolve(ResolveMode mode) {
PLOG_VERBOSE << "Resolving candidate (mode="
<< (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mNode << ' '
<< mService;
struct addrinfo *result = nullptr;
if (getaddrinfo(node.c_str(), service.c_str(), &hints, &result) == 0) {
for (auto p = result; p; p = p->ai_next) {
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
// Rewrite the candidate
char nodebuffer[MAX_NUMERICNODE_LEN];
char servbuffer[MAX_NUMERICSERV_LEN];
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
// Try to resolve the node and service
struct addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
if (mTransportType == TransportType::Udp) {
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
} else if (mTransportType != TransportType::Unknown) {
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
}
mAddress = nodebuffer;
mPort = uint16_t(std::stoul(servbuffer));
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
if (mode == ResolveMode::Simple)
hints.ai_flags |= AI_NUMERICHOST;
const char sp{' '};
std::ostringstream oss;
oss << foundation << sp << component << sp << transport << sp << priority;
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
oss << left;
mCandidate = oss.str();
struct addrinfo *result = nullptr;
if (getaddrinfo(mNode.c_str(), mService.c_str(), &hints, &result) == 0) {
for (auto p = result; p; p = p->ai_next) {
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
char nodebuffer[MAX_NUMERICNODE_LEN];
char servbuffer[MAX_NUMERICSERV_LEN];
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
break;
}
mAddress = nodebuffer;
mPort = uint16_t(std::stoul(servbuffer));
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
break;
}
}
freeaddrinfo(result);
}
freeaddrinfo(result);
}
return mFamily != Family::Unresolved;
}
string Candidate::candidate() const { return "candidate:" + mCandidate; }
Candidate::Type Candidate::type() const { return mType; }
Candidate::TransportType Candidate::transportType() const { return mTransportType; }
uint32_t Candidate::priority() const { return mPriority; }
string Candidate::candidate() const {
const char sp{' '};
std::ostringstream oss;
oss << "candidate:";
oss << mFoundation << sp << mComponent << sp << mTransportString << sp << mPriority << sp;
if (isResolved())
oss << mAddress << sp << mPort;
else
oss << mNode << sp << mService;
oss << sp << "typ" << sp << mTypeString;
if (!mTail.empty())
oss << sp << mTail;
return oss.str();
}
string Candidate::mid() const { return mMid.value_or("0"); }
@ -186,14 +215,18 @@ Candidate::operator string() const {
return line.str();
}
bool Candidate::operator==(const Candidate &other) const {
return mFoundation == other.mFoundation;
}
bool Candidate::operator!=(const Candidate &other) const {
return mFoundation != other.mFoundation;
}
bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
Candidate::Family Candidate::family() const { return mFamily; }
Candidate::Type Candidate::type() const { return mType; }
Candidate::TransportType Candidate::transportType() const { return mTransportType; }
std::optional<string> Candidate::address() const {
return isResolved() ? std::make_optional(mAddress) : nullopt;
}
@ -202,10 +235,6 @@ std::optional<uint16_t> Candidate::port() const {
return isResolved() ? std::make_optional(mPort) : nullopt;
}
std::optional<uint32_t> Candidate::priority() const {
return isResolved() ? std::make_optional(mPriority) : nullopt;
}
} // namespace rtc
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate) {
@ -217,11 +246,11 @@ std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type) {
case rtc::Candidate::Type::Host:
return out << "host";
case rtc::Candidate::Type::PeerReflexive:
return out << "peer_reflexive";
return out << "prflx";
case rtc::Candidate::Type::ServerReflexive:
return out << "server_reflexive";
return out << "srflx";
case rtc::Candidate::Type::Relayed:
return out << "relayed";
return out << "relay";
default:
return out << "unknown";
}

View File

@ -306,7 +306,7 @@ void NegociatedDataChannel::processOpenMessage(message_ptr message) {
mLabel.assign(end, open.labelLength);
mProtocol.assign(end + open.labelLength, open.protocolLength);
mReliability->unordered = (open.reliabilityParameter & 0x80) != 0;
mReliability->unordered = (open.channelType & 0x80) != 0;
switch (open.channelType & 0x7F) {
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
mReliability->type = Reliability::Type::Rexmit;

View File

@ -171,16 +171,27 @@ void Description::setFingerprint(string fingerprint) {
mFingerprint.emplace(std::move(fingerprint));
}
bool Description::hasCandidate(const Candidate &candidate) const {
for (const Candidate &other : mCandidates)
if (candidate == other)
return true;
return false;
}
void Description::addCandidate(Candidate candidate) {
candidate.hintMid(bundleMid());
for (const Candidate &other : mCandidates)
if (candidate == other)
return;
mCandidates.emplace_back(std::move(candidate));
}
void Description::addCandidates(std::vector<Candidate> candidates) {
for (Candidate candidate : candidates) {
candidate.hintMid(bundleMid());
mCandidates.emplace_back(std::move(candidate));
}
for (Candidate candidate : candidates)
addCandidate(std::move(candidate));
}
void Description::endCandidates() { mEnded = true; }

View File

@ -141,7 +141,14 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
}
message->resize(size);
return outgoing(message);
if (message->dscp == 0) { // Track might override the value
// Set recommended medium-priority DSCP value
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
}
return Transport::outgoing(message); // bypass DTLS DSCP marking
}
void DtlsSrtpTransport::incoming(message_ptr message) {

View File

@ -53,7 +53,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
verifier_callback verifierCallback, state_callback stateChangeCallback)
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
mVerifierCallback(std::move(verifierCallback)),
mIsClient(lower->role() == Description::Role::Active) {
mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
@ -122,6 +122,7 @@ bool DtlsTransport::send(message_ptr message) {
PLOG_VERBOSE << "Send size=" << message->size();
mCurrentDscp = message->dscp;
ssize_t ret;
do {
ret = gnutls_record_send(mSession, message->data(), message->size());
@ -143,6 +144,13 @@ void DtlsTransport::incoming(message_ptr message) {
mIncomingQueue.push(message);
}
bool DtlsTransport::outgoing(message_ptr message) {
if (message->dscp == 0)
message->dscp = mCurrentDscp;
return Transport::outgoing(std::move(message));
}
void DtlsTransport::postHandshake() {
// Dummy
}
@ -309,7 +317,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
verifier_callback verifierCallback, state_callback stateChangeCallback)
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
mVerifierCallback(std::move(verifierCallback)),
mIsClient(lower->role() == Description::Role::Active) {
mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
try {
@ -405,6 +413,7 @@ bool DtlsTransport::send(message_ptr message) {
PLOG_VERBOSE << "Send size=" << message->size();
mCurrentDscp = message->dscp;
int ret = SSL_write(mSsl, message->data(), int(message->size()));
return openssl::check(mSsl, ret);
}
@ -419,6 +428,13 @@ void DtlsTransport::incoming(message_ptr message) {
mIncomingQueue.push(message);
}
bool DtlsTransport::outgoing(message_ptr message) {
if (message->dscp == 0)
message->dscp = mCurrentDscp;
return Transport::outgoing(std::move(message));
}
void DtlsTransport::postHandshake() {
// Dummy
}

View File

@ -53,6 +53,7 @@ public:
protected:
virtual void incoming(message_ptr message) override;
virtual bool outgoing(message_ptr message) override;
virtual void postHandshake();
void runRecvLoop();
@ -62,6 +63,7 @@ protected:
Queue<message_ptr> mIncomingQueue;
std::thread mRecvThread;
std::atomic<unsigned int> mCurrentDscp;
#if USE_GNUTLS
gnutls_session_t mSession;

View File

@ -222,8 +222,10 @@ bool IceTransport::send(message_ptr message) {
}
bool IceTransport::outgoing(message_ptr message) {
return juice_send(mAgent.get(), reinterpret_cast<const char *>(message->data()),
message->size()) >= 0;
// Explicit Congestion Notification takes the least-significant 2 bits of the DS field
int ds = int(message->dscp << 2);
return juice_send_diffserv(mAgent.get(), reinterpret_cast<const char *>(message->data()),
message->size(), ds) >= 0;
}
void IceTransport::changeGatheringState(GatheringState state) {
@ -330,7 +332,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
mMid("0"), mGatheringState(GatheringState::New),
mCandidateCallback(std::move(candidateCallback)),
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr) {
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr), mOutgoingDscp(0) {
PLOG_DEBUG << "Initializing ICE transport (libnice)";
@ -564,12 +566,14 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
return false;
// Warning: the candidate string must start with "a=candidate:" and it must not end with a
// newline, else libnice will reject it.
// newline or whitespace, else libnice will reject it.
string sdp(candidate);
NiceCandidate *cand =
nice_agent_parse_remote_candidate_sdp(mNiceAgent.get(), mStreamId, sdp.c_str());
if (!cand)
if (!cand) {
PLOG_WARNING << "Rejected ICE candidate: " << sdp;
return false;
}
GSList *list = g_slist_append(nullptr, cand);
int ret = nice_agent_set_remote_candidates(mNiceAgent.get(), mStreamId, 1, list);
@ -615,6 +619,13 @@ bool IceTransport::send(message_ptr message) {
}
bool IceTransport::outgoing(message_ptr message) {
std::lock_guard lock(mOutgoingMutex);
if (mOutgoingDscp != message->dscp) {
mOutgoingDscp = message->dscp;
// Explicit Congestion Notification takes the least-significant 2 bits of the DS field
int ds = int(message->dscp << 2);
nice_agent_set_stream_tos(mNiceAgent.get(), mStreamId, ds); // ToS is the legacy name for DS
}
return nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
reinterpret_cast<const char *>(message->data())) >= 0;
}

View File

@ -35,6 +35,7 @@
#include <atomic>
#include <chrono>
#include <thread>
#include <mutex>
namespace rtc {
@ -99,6 +100,8 @@ private:
std::unique_ptr<GMainLoop, void (*)(GMainLoop *)> mMainLoop;
std::thread mMainLoopThread;
guint mTimeoutId = 0;
std::mutex mOutgoingMutex;
unsigned int mOutgoingDscp;
static string AddressToString(const NiceAddress &addr);

View File

@ -266,10 +266,6 @@ void PeerConnection::setRemoteDescription(Description description) {
// Candidates will be added at the end, extract them for now
auto remoteCandidates = description.extractCandidates();
auto type = description.type();
auto iceTransport = initIceTransport();
iceTransport->setRemoteDescription(description);
processRemoteDescription(std::move(description));
changeSignalingState(newSignalingState);
@ -279,8 +275,9 @@ void PeerConnection::setRemoteDescription(Description description) {
setLocalDescription(Description::Type::Answer);
} else {
// This is an answer
auto iceTransport = std::atomic_load(&mIceTransport);
auto sctpTransport = std::atomic_load(&mSctpTransport);
if (!sctpTransport && iceTransport->role() == Description::Role::Active) {
if (!sctpTransport && iceTransport && iceTransport->role() == Description::Role::Active) {
// Since we assumed passive role during DataChannel creation, we need to shift the
// stream numbers by one to shift them from odd to even.
std::unique_lock lock(mDataChannelsMutex); // we are going to swap the container
@ -372,7 +369,7 @@ void PeerConnection::onSignalingStateChange(std::function<void(SignalingState st
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
#if !RTC_ENABLE_MEDIA
if (mTracks.empty()) {
PLOG_WARNING << "Tracks will be inative (not compiled with SRTP support)";
PLOG_WARNING << "Tracks will be inative (not compiled with media support)";
}
#endif
@ -506,7 +503,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
lower, certificate, verifierCallback,
std::bind(&PeerConnection::forwardMedia, this, _1), stateChangeCallback);
#else
PLOG_WARNING << "Ignoring media support (not compiled with SRTP support)";
PLOG_WARNING << "Ignoring media support (not compiled with media support)";
#endif
}
@ -875,7 +872,7 @@ void PeerConnection::incomingTrack(Description::Media description) {
std::unique_lock lock(mTracksMutex); // we are going to emplace
#if !RTC_ENABLE_MEDIA
if (mTracks.empty()) {
PLOG_WARNING << "Tracks will be inative (not compiled with SRTP support)";
PLOG_WARNING << "Tracks will be inative (not compiled with media support)";
}
#endif
if (mTracks.find(description.mid()) == mTracks.end()) {
@ -1079,7 +1076,7 @@ void PeerConnection::processLocalCandidate(Candidate candidate) {
if (!mLocalDescription)
throw std::logic_error("Got a local candidate without local description");
candidate.resolve(Candidate::ResolveMode::Simple); // for proper SDP generation later
candidate.resolve(Candidate::ResolveMode::Simple);
mLocalDescription->addCandidate(candidate);
PLOG_VERBOSE << "Issuing local candidate: " << candidate;
@ -1095,10 +1092,13 @@ void PeerConnection::processRemoteDescription(Description description) {
if (mRemoteDescription)
existingCandidates = mRemoteDescription->extractCandidates();
mRemoteDescription.emplace(std::move(description));
mRemoteDescription.emplace(description);
mRemoteDescription->addCandidates(std::move(existingCandidates));
}
auto iceTransport = initIceTransport();
iceTransport->setRemoteDescription(std::move(description));
if (description.hasApplication()) {
auto dtlsTransport = std::atomic_load(&mDtlsTransport);
auto sctpTransport = std::atomic_load(&mSctpTransport);
@ -1109,28 +1109,40 @@ void PeerConnection::processRemoteDescription(Description description) {
}
void PeerConnection::processRemoteCandidate(Candidate candidate) {
std::lock_guard lock(mRemoteDescriptionMutex);
auto iceTransport = std::atomic_load(&mIceTransport);
if (!mRemoteDescription || !iceTransport)
throw std::logic_error("Got a remote candidate without remote description");
{
// Set as remote candidate
std::lock_guard lock(mRemoteDescriptionMutex);
if (!mRemoteDescription)
throw std::logic_error("Got a remote candidate without remote description");
candidate.hintMid(mRemoteDescription->bundleMid());
if (!iceTransport)
throw std::logic_error("Got a remote candidate without ICE transport");
if (candidate.resolve(Candidate::ResolveMode::Simple)) {
iceTransport->addRemoteCandidate(candidate);
} else {
// OK, we might need a lookup, do it asynchronously
// We don't use the thread pool because we have no control on the timeout
weak_ptr<IceTransport> weakIceTransport{iceTransport};
std::thread t([weakIceTransport, candidate]() mutable {
if (candidate.resolve(Candidate::ResolveMode::Lookup))
if (auto iceTransport = weakIceTransport.lock())
iceTransport->addRemoteCandidate(candidate);
});
t.detach();
candidate.hintMid(mRemoteDescription->bundleMid());
if (mRemoteDescription->hasCandidate(candidate))
return; // already in description, ignore
candidate.resolve(Candidate::ResolveMode::Simple);
mRemoteDescription->addCandidate(candidate);
}
mRemoteDescription->addCandidate(std::move(candidate));
if (candidate.isResolved()) {
iceTransport->addRemoteCandidate(std::move(candidate));
} else {
// We might need a lookup, do it asynchronously
// We don't use the thread pool because we have no control on the timeout
if (auto iceTransport = std::atomic_load(&mIceTransport)) {
weak_ptr<IceTransport> weakIceTransport{iceTransport};
std::thread t([weakIceTransport, candidate = std::move(candidate)]() mutable {
if (candidate.resolve(Candidate::ResolveMode::Lookup))
if (auto iceTransport = weakIceTransport.lock())
iceTransport->addRemoteCandidate(std::move(candidate));
});
t.detach();
}
}
}
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {

View File

@ -88,7 +88,7 @@ void SctpTransport::Cleanup() {
SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
message_callback recvCallback, amount_callback bufferedAmountCallback,
state_callback stateChangeCallback)
: Transport(lower, std::move(stateChangeCallback)), mPort(port),
: Transport(lower, std::move(stateChangeCallback)), mPort(port), mPendingRecvCount(0),
mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
onRecv(recvCallback);
@ -325,8 +325,16 @@ void SctpTransport::incoming(message_ptr message) {
usrsctp_conninput(this, message->data(), message->size(), 0);
}
bool SctpTransport::outgoing(message_ptr message) {
// Set recommended medium-priority DSCP value
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
message->dscp = 10; // AF11: Assured Forwarding class 1, low drop probability
return Transport::outgoing(std::move(message));
}
void SctpTransport::doRecv() {
std::lock_guard lock(mRecvMutex);
--mPendingRecvCount;
try {
while (true) {
const size_t bufferSize = 65536;
@ -532,15 +540,17 @@ bool SctpTransport::safeFlush() {
}
void SctpTransport::handleUpcall() {
if(!mSock)
if (!mSock)
return;
PLOG_VERBOSE << "Handle upcall";
int events = usrsctp_get_events(mSock);
if (events & SCTP_EVENT_READ)
if (events & SCTP_EVENT_READ && mPendingRecvCount == 0) {
++mPendingRecvCount;
mProcessor.enqueue(&SctpTransport::doRecv, this);
}
if (events & SCTP_EVENT_WRITE)
mProcessor.enqueue(&SctpTransport::safeFlush, this);
@ -551,6 +561,7 @@ int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t
std::unique_lock lock(mWriteMutex);
PLOG_VERBOSE << "Handle write, len=" << len;
auto message = make_message(data, data + len);
if (!outgoing(make_message(data, data + len)))
return -1;

View File

@ -77,6 +77,7 @@ private:
void shutdown();
void close();
void incoming(message_ptr message) override;
bool outgoing(message_ptr message) override;
void doRecv();
bool trySendQueue();
@ -95,6 +96,7 @@ private:
struct socket *mSock;
Processor mProcessor;
std::atomic<int> mPendingRecvCount;
std::mutex mRecvMutex, mSendMutex;
Queue<message_ptr> mSendQueue;
std::map<uint16_t, size_t> mBufferedAmount;

View File

@ -45,12 +45,30 @@ void Track::close() {
setRtcpHandler(nullptr);
}
bool Track::send(message_variant data) { return outgoing(make_message(std::move(data))); }
bool Track::send(message_variant data) {
if (mIsClosed)
throw std::runtime_error("Track is closed");
bool Track::send(const byte *data, size_t size) {
return outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
auto direction = mMediaDescription.direction();
if ((direction == Description::Direction::RecvOnly ||
direction == Description::Direction::Inactive)) {
PLOG_WARNING << "Track media direction does not allow transmission, dropping";
return false;
}
auto message = make_message(std::move(data));
if (mRtcpHandler) {
message = mRtcpHandler->outgoing(message);
if (!message)
return false;
}
return outgoing(std::move(message));
}
bool Track::send(const byte *data, size_t size) { return send(binary(data, data + size)); }
std::optional<message_variant> Track::receive() {
if (auto next = mRecvQueue.tryPop())
return to_variant(std::move(**next));
@ -88,50 +106,10 @@ void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
}
#endif
bool Track::outgoing(message_ptr message) {
if (mRtcpHandler) {
message = mRtcpHandler->outgoing(message);
if (!message)
return false;
}
auto direction = mMediaDescription.direction();
if ((direction == Description::Direction::RecvOnly ||
direction == Description::Direction::Inactive) &&
message->type != Message::Control) {
PLOG_WARNING << "Track media direction does not allow transmission, dropping";
return false;
}
if (mIsClosed)
throw std::runtime_error("Track is closed");
if (message->size() > maxMessageSize())
throw std::runtime_error("Message size exceeds limit");
#if RTC_ENABLE_MEDIA
auto transport = mDtlsSrtpTransport.lock();
if (!transport)
throw std::runtime_error("Track transport is not open");
return transport->sendMedia(message);
#else
PLOG_WARNING << "Ignoring track send (not compiled with SRTP support)";
return false;
#endif
}
void Track::incoming(message_ptr message) {
if (!message)
return;
if (mRtcpHandler) {
message = mRtcpHandler->incoming(message);
if (!message)
return;
}
auto direction = mMediaDescription.direction();
if ((direction == Description::Direction::SendOnly ||
direction == Description::Direction::Inactive) &&
@ -140,30 +118,46 @@ void Track::incoming(message_ptr message) {
return;
}
if (mRtcpHandler) {
message = mRtcpHandler->incoming(message);
if (!message)
return;
}
// Tail drop if queue is full
if (mRecvQueue.full())
if (mRecvQueue.full()) {
PLOG_WARNING << "Track incoming queue is full, dropping";
return;
}
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
}
bool Track::outgoing([[maybe_unused]] message_ptr message) {
#if RTC_ENABLE_MEDIA
auto transport = mDtlsSrtpTransport.lock();
if (!transport)
throw std::runtime_error("Track transport is not open");
// Set recommended medium-priority DSCP value
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
if (mMediaDescription.type() == "audio")
message->dscp = 46; // EF: Expedited Forwarding
else
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
return transport->sendMedia(message);
#else
PLOG_WARNING << "Ignoring track send (not compiled with media support)";
return false;
#endif
}
void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
mRtcpHandler = std::move(handler);
if (mRtcpHandler) {
mRtcpHandler->onOutgoing([&]([[maybe_unused]] const rtc::message_ptr &message) {
#if RTC_ENABLE_MEDIA
auto transport = mDtlsSrtpTransport.lock();
if (!transport)
throw std::runtime_error("Track transport is not open");
return transport->sendMedia(message);
#else
PLOG_WARNING << "Ignoring track send (not compiled with SRTP support)";
return false;
#endif
});
}
if (mRtcpHandler)
mRtcpHandler->onOutgoing(std::bind(&Track::outgoing, this, std::placeholders::_1));
}
bool Track::requestKeyframe() {

View File

@ -28,8 +28,6 @@
namespace rtc {
using namespace std::placeholders;
class Transport {
public:
enum class State { Disconnected, Connecting, Connected, Completed, Failed };
@ -57,7 +55,7 @@ public:
void registerIncoming() {
if (mLower) {
PLOG_VERBOSE << "Registering incoming callback";
mLower->onRecv(std::bind(&Transport::incoming, this, _1));
mLower->onRecv(std::bind(&Transport::incoming, this, std::placeholders::_1));
}
}

View File

@ -36,6 +36,7 @@
namespace rtc {
using std::shared_ptr;
using namespace std::placeholders;
WebSocket::WebSocket(std::optional<Configuration> config)
: mConfig(config ? std::move(*config) : Configuration()),

View File

@ -100,14 +100,48 @@ static void RTC_API messageCallback(int id, const char *message, int size, void
static void RTC_API dataChannelCallback(int pc, int dc, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->dc = dc;
peer->connected = true;
char label[256];
if (rtcGetDataChannelLabel(dc, label, 256) < 0) {
fprintf(stderr, "rtcGetDataChannelLabel failed\n");
return;
}
char protocol[256];
if (rtcGetDataChannelProtocol(dc, protocol, 256) < 0) {
fprintf(stderr, "rtcGetDataChannelProtocol failed\n");
return;
}
rtcReliability reliability;
if (rtcGetDataChannelReliability(dc, &reliability) < 0) {
fprintf(stderr, "rtcGetDataChannelReliability failed\n");
return;
}
printf("DataChannel %d: Received with label \"%s\" and protocol \"%s\"\n",
peer == peer1 ? 1 : 2, label, protocol);
if (strcmp(label, "test") != 0) {
fprintf(stderr, "Wrong DataChannel label\n");
return;
}
if (strcmp(protocol, "protocol") != 0) {
fprintf(stderr, "Wrong DataChannel protocol\n");
return;
}
if (reliability.unordered == false) {
fprintf(stderr, "Wrong DataChannel reliability\n");
return;
}
rtcSetClosedCallback(dc, closedCallback);
rtcSetMessageCallback(dc, messageCallback);
char buffer[256];
if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
printf("DataChannel %d: Received with label \"%s\"\n", peer == peer1 ? 1 : 2, buffer);
peer->dc = dc;
peer->connected = true;
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
@ -127,6 +161,7 @@ static Peer *createPeer(const rtcConfiguration *config) {
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
rtcSetSignalingStateChangeCallback(peer->pc, signalingStateCallback);
return peer;
}
@ -173,7 +208,12 @@ int test_capi_connectivity_main() {
goto error;
// Peer 1: Create data channel
peer1->dc = rtcCreateDataChannel(peer1->pc, "test");
rtcDataChannelInit init;
memset(&init, 0, sizeof(init));
init.protocol = "protocol";
init.reliability.unordered = true;
peer1->dc = rtcCreateDataChannelEx(peer1->pc, "test", &init);
rtcSetOpenCallback(peer1->dc, openCallback);
rtcSetClosedCallback(peer1->dc, closedCallback);
rtcSetMessageCallback(peer1->dc, messageCallback);