mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
4cdb788124 | |||
6255ff1995 | |||
1d47294050 | |||
86f93e9aa3 | |||
d218e7923c | |||
2d97cc18c6 | |||
76d2d3d97f | |||
1156fbe434 | |||
983b5759f3 | |||
79242e27ed | |||
39e51a8345 | |||
f4aae34874 | |||
34469d16ae | |||
e6a9650523 | |||
a9057a02e5 | |||
a2b019465c | |||
8bdce69ab7 | |||
78ca3a318f | |||
e88197646d | |||
c5e4b972c2 | |||
4cdde18e4b | |||
41cba8a35a | |||
6c683b326d | |||
a152edf256 | |||
8f50eeb0f2 | |||
1105a4faec | |||
8184d1d60e | |||
38e1a946b0 | |||
8522446d6c | |||
1cfefd9dcd | |||
fbe141301c | |||
811a6b8a26 | |||
ddbd963e7e | |||
05a37c8306 | |||
a9ca8b687b | |||
e91880141a | |||
5c8d63ad78 | |||
c5cb81762c | |||
0b50fc4bb0 |
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(libdatachannel
|
||||
VERSION 0.13.2
|
||||
VERSION 0.13.3
|
||||
LANGUAGES CXX)
|
||||
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
|
||||
|
||||
|
1
DOC.md
1
DOC.md
@ -86,6 +86,7 @@ Arguments:
|
||||
- `config`: the configuration structure, containing:
|
||||
- `iceServers` (optional): an array of pointers on null-terminated ice server URIs (NULL if unused)
|
||||
- `iceServersCount` (optional): number of URLs in the array pointed by `iceServers` (0 if unused)
|
||||
- `bindAddress` (optional): if non-NULL, bind only to the given local address (ignored with libnice as ICE backend)
|
||||
- `certificateType` (optional): certificate type, either `RTC_CERTIFICATE_ECDSA` or `RTC_CERTIFICATE_RSA` (0 or `RTC_CERTIFICATE_DEFAULT` if default)
|
||||
- `enableIceTcp`: if true, generate TCP candidates for ICE (ignored with libjuice as ICE backend)
|
||||
- `disableAutoNegotiation`: if true, the user is responsible for calling `rtcSetLocalDescription` after creating a Data Channel and after setting the remote description
|
||||
|
@ -1,4 +1,4 @@
|
||||
# libdatachannel - C/C++ WebRTC Data Channels
|
||||
# libdatachannel - C/C++ WebRTC lightweight library
|
||||
|
||||
libdatachannel is a standalone implementation of WebRTC Data Channels, WebRTC Media Transport, and WebSockets in C++17 with C bindings for POSIX platforms (including GNU/Linux, Android, and Apple macOS) and Microsoft Windows.
|
||||
|
||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: c65fea04f0...15d6654262
@ -3,7 +3,7 @@
|
||||
This directory contains a native client to open Data Channels with WebSocket signaling using libdatachannel and benchmark functionalities. It offers three functionalities;
|
||||
- Benchmark: Bi-directional data transfer benchmark (Also supports One-Way testing)
|
||||
- Constant Throughput Set: Send desired amount of data per second
|
||||
- Multiple Data Channel: Create desried count of data channel
|
||||
- Multiple Data Channel: Create desired amount of data channel
|
||||
|
||||
## Start Signaling Server
|
||||
- Start one of the signaling server from the examples folder. For example start `signaling-server-nodejs` like;
|
||||
@ -460,4 +460,4 @@ Stats# Received Total: 343 MB Sent Total: 372 MB RTT: 16 ms
|
||||
DC-1 Received: 7972 KB/s Sent: 8001 KB/s BufferSize: 82497
|
||||
TOTL Received: 39894 KB/s Sent: 40005 KB/s
|
||||
Stats# Received Total: 538 MB Sent Total: 581 MB RTT: 3 ms
|
||||
```
|
||||
```
|
||||
|
@ -74,6 +74,7 @@ struct RTC_CPP_EXPORT Configuration {
|
||||
// ICE settings
|
||||
std::vector<IceServer> iceServers;
|
||||
optional<ProxyServer> proxyServer; // libnice only
|
||||
optional<string> bindAddress; // libjuice only, default any
|
||||
|
||||
// Options
|
||||
CertificateType certificateType = CertificateType::Default;
|
||||
|
@ -144,12 +144,14 @@ public:
|
||||
|
||||
void removeFormat(const string &fmt);
|
||||
|
||||
void addSSRC(uint32_t ssrc, optional<string> name,
|
||||
optional<string> msid = nullopt, optional<string> trackID = nullopt);
|
||||
void addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid = nullopt,
|
||||
optional<string> trackID = nullopt);
|
||||
void removeSSRC(uint32_t oldSSRC);
|
||||
void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
||||
optional<string> msid = nullopt, optional<string> trackID = nullopt);
|
||||
optional<string> msid = nullopt, optional<string> trackID = nullopt);
|
||||
bool hasSSRC(uint32_t ssrc);
|
||||
std::vector<uint32_t> getSSRCs();
|
||||
std::optional<std::string> getCNameForSsrc(uint32_t ssrc);
|
||||
|
||||
void setBitrate(int bitrate);
|
||||
int getBitrate() const;
|
||||
@ -181,6 +183,8 @@ public:
|
||||
void setMLine(string_view view);
|
||||
};
|
||||
|
||||
void addRTPMap(const RTPMap &map);
|
||||
|
||||
std::map<int, RTPMap>::iterator beginMaps();
|
||||
std::map<int, RTPMap>::iterator endMaps();
|
||||
std::map<int, RTPMap>::iterator removeMap(std::map<int, RTPMap>::iterator iterator);
|
||||
@ -195,33 +199,25 @@ public:
|
||||
|
||||
std::map<int, RTPMap> mRtpMap;
|
||||
std::vector<uint32_t> mSsrcs;
|
||||
|
||||
public:
|
||||
void addRTPMap(const RTPMap &map);
|
||||
|
||||
void removeSSRC(uint32_t oldSSRC);
|
||||
std::map<uint32_t, string> mCNameMap;
|
||||
};
|
||||
|
||||
class RTC_CPP_EXPORT Audio : public Media {
|
||||
public:
|
||||
Audio(string mid = "audio", Direction dir = Direction::SendOnly);
|
||||
|
||||
void addAudioCodec(int payloadType, string codec,
|
||||
optional<string> profile = std::nullopt);
|
||||
void addAudioCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
|
||||
|
||||
void addOpusCodec(int payloadType,
|
||||
optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
|
||||
void addOpusCodec(int payloadType, optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
|
||||
};
|
||||
|
||||
class RTC_CPP_EXPORT Video : public Media {
|
||||
public:
|
||||
Video(string mid = "video", Direction dir = Direction::SendOnly);
|
||||
|
||||
void addVideoCodec(int payloadType, string codec,
|
||||
optional<string> profile = std::nullopt);
|
||||
void addVideoCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
|
||||
|
||||
void addH264Codec(int payloadType,
|
||||
optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
|
||||
void addH264Codec(int payloadType, optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
|
||||
void addVP8Codec(int payloadType);
|
||||
void addVP9Codec(int payloadType);
|
||||
};
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "common.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace rtc {
|
||||
|
||||
@ -38,12 +39,12 @@ enum class LogLevel { // Don't change, it must match plog severity
|
||||
typedef std::function<void(LogLevel level, string message)> LogCallback;
|
||||
|
||||
RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr);
|
||||
#ifdef PLOG
|
||||
#ifdef PLOG_DEFAULT_INSTANCE_ID
|
||||
RTC_CPP_EXPORT void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
|
||||
#endif
|
||||
|
||||
RTC_EXPORT void Preload();
|
||||
RTC_EXPORT void Cleanup();
|
||||
RTC_CPP_EXPORT void Preload();
|
||||
RTC_CPP_EXPORT void Cleanup();
|
||||
|
||||
struct SctpSettings {
|
||||
// For the following settings, not set means optimized default
|
||||
@ -54,10 +55,17 @@ struct SctpSettings {
|
||||
optional<size_t> maxBurst; // in MTUs
|
||||
optional<unsigned int> congestionControlModule; // 0: RFC2581, 1: HSTCP, 2: H-TCP, 3: RTCC
|
||||
optional<std::chrono::milliseconds> delayedSackTime;
|
||||
optional<std::chrono::milliseconds> minRetransmitTimeout;
|
||||
optional<std::chrono::milliseconds> maxRetransmitTimeout;
|
||||
optional<std::chrono::milliseconds> initialRetransmitTimeout;
|
||||
optional<unsigned int> maxRetransmitAttempts;
|
||||
optional<std::chrono::milliseconds> heartbeatInterval;
|
||||
};
|
||||
|
||||
RTC_EXPORT void SetSctpSettings(SctpSettings s);
|
||||
RTC_CPP_EXPORT void SetSctpSettings(SctpSettings s);
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::LogLevel level);
|
||||
|
||||
#endif
|
||||
|
@ -150,13 +150,14 @@ RTC_EXPORT void *rtcGetUserPointer(int i);
|
||||
typedef struct {
|
||||
const char **iceServers;
|
||||
int iceServersCount;
|
||||
const char *bindAddress; // libjuice only, NULL means any
|
||||
rtcCertificateType certificateType;
|
||||
bool enableIceTcp;
|
||||
bool disableAutoNegotiation;
|
||||
uint16_t portRangeBegin;
|
||||
uint16_t portRangeEnd;
|
||||
int mtu; // <= 0 means automatic
|
||||
int maxMessageSize; // <= 0 means default
|
||||
uint16_t portRangeBegin; // 0 means automatic
|
||||
uint16_t portRangeEnd; // 0 means automatic
|
||||
int mtu; // <= 0 means automatic
|
||||
int maxMessageSize; // <= 0 means default
|
||||
} rtcConfiguration;
|
||||
|
||||
RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
|
||||
@ -205,6 +206,7 @@ RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
|
||||
RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
|
||||
RTC_EXPORT int rtcCreateDataChannelEx(int pc, const char *label,
|
||||
const rtcDataChannelInit *init); // returns dc id
|
||||
RTC_EXPORT int rtcIsOpen(int dc);
|
||||
RTC_EXPORT int rtcDeleteDataChannel(int dc);
|
||||
|
||||
RTC_EXPORT int rtcGetDataChannelStream(int dc);
|
||||
@ -253,6 +255,13 @@ typedef struct {
|
||||
uint32_t timestamp; // Start timestamp
|
||||
} rtcStartTime;
|
||||
|
||||
typedef struct {
|
||||
uint32_t ssrc;
|
||||
const char *name; // optional
|
||||
const char *msid; // optional
|
||||
const char *trackId; // optional, track ID used in MSID
|
||||
} rtcSsrcForTypeInit;
|
||||
|
||||
// Set H264PacketizationHandler for track
|
||||
RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init);
|
||||
|
||||
@ -297,6 +306,22 @@ RTC_EXPORT int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timest
|
||||
// Set NeedsToReport flag in RtcpSrReporter handler identified by given track id
|
||||
RTC_EXPORT int rtcSetNeedsToSendRtcpSr(int id);
|
||||
|
||||
/// Get all available payload types for given codec and stores them in buffer, does nothing if buffer is NULL
|
||||
int rtcGetTrackPayloadTypesForCodec(int tr, const char * ccodec, int * buffer, int size);
|
||||
|
||||
/// Get all SSRCs for given track
|
||||
int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int count);
|
||||
|
||||
/// Get CName for SSRC
|
||||
int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char * cname, int cnameSize);
|
||||
|
||||
/// Get all SSRCs for given media type in given SDP
|
||||
/// @param mediaType Media type (audio/video)
|
||||
int rtcGetSsrcsForType(const char * mediaType, const char * sdp, uint32_t * buffer, int bufferSize);
|
||||
|
||||
/// Set SSRC for given media type in given SDP
|
||||
int rtcSetSsrcForType(const char * mediaType, const char * sdp, char * buffer, const int bufferSize, rtcSsrcForTypeInit * init);
|
||||
|
||||
#endif // RTC_ENABLE_MEDIA
|
||||
|
||||
#if RTC_ENABLE_WEBSOCKET
|
||||
@ -339,13 +364,18 @@ RTC_EXPORT void rtcCleanup(void);
|
||||
// SCTP global settings
|
||||
|
||||
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 msecs, <= 0 means optimized default
|
||||
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 msecs, 0 means optimized default, < 0 means disabled
|
||||
int minRetransmitTimeoutMs; // in msecs, <= 0 means optimized default
|
||||
int maxRetransmitTimeoutMs; // in msecs, <= 0 means optimized default
|
||||
int initialRetransmitTimeoutMs; // in msecs, <= 0 means optimized default
|
||||
int maxRetransmitAttempts; // number of retransmissions, <= 0 means optimized default
|
||||
int heartbeatIntervalMs; // in msecs, <= 0 means optimized default
|
||||
} rtcSctpSettings;
|
||||
|
||||
// Note: SCTP settings apply to newly-created PeerConnections only
|
||||
|
149
src/capi.cpp
149
src/capi.cpp
@ -268,6 +268,24 @@ int copyAndReturn(binary b, char *buffer, int size) {
|
||||
return int(b.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int copyAndReturn(std::vector<T> b, T *buffer, int size) {
|
||||
if (!buffer)
|
||||
return int(b.size());
|
||||
|
||||
if (size < int(b.size()))
|
||||
return RTC_ERR_TOO_SMALL;
|
||||
std::copy(b.begin(), b.end(), buffer);
|
||||
return int(b.size());
|
||||
}
|
||||
|
||||
#if RTC_ENABLE_MEDIA
|
||||
// function is used in RTC_ENABLE_MEDIA only
|
||||
string lowercased(string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
return str;
|
||||
}
|
||||
#endif // RTC_ENABLE_MEDIA
|
||||
} // namespace
|
||||
|
||||
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
|
||||
@ -290,15 +308,18 @@ int rtcCreatePeerConnection(const rtcConfiguration *config) {
|
||||
for (int i = 0; i < config->iceServersCount; ++i)
|
||||
c.iceServers.emplace_back(string(config->iceServers[i]));
|
||||
|
||||
c.certificateType = static_cast<CertificateType>(config->certificateType);
|
||||
c.enableIceTcp = config->enableIceTcp;
|
||||
c.disableAutoNegotiation = config->disableAutoNegotiation;
|
||||
if (config->bindAddress)
|
||||
c.bindAddress = string(config->bindAddress);
|
||||
|
||||
if (config->portRangeBegin > 0 || config->portRangeEnd > 0) {
|
||||
c.portRangeBegin = config->portRangeBegin;
|
||||
c.portRangeEnd = config->portRangeEnd;
|
||||
}
|
||||
|
||||
c.certificateType = static_cast<CertificateType>(config->certificateType);
|
||||
c.enableIceTcp = config->enableIceTcp;
|
||||
c.disableAutoNegotiation = config->disableAutoNegotiation;
|
||||
|
||||
if (config->mtu > 0)
|
||||
c.mtu = size_t(config->mtu);
|
||||
|
||||
@ -362,6 +383,12 @@ int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *
|
||||
});
|
||||
}
|
||||
|
||||
int rtcIsOpen(int cid) {
|
||||
return wrap([cid] {
|
||||
return getChannel(cid)->isOpen();
|
||||
});
|
||||
}
|
||||
|
||||
int rtcDeleteDataChannel(int dc) {
|
||||
return wrap([dc] {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
@ -500,6 +527,26 @@ int rtcGetTrackDescription(int tr, char *buffer, int size) {
|
||||
|
||||
#if RTC_ENABLE_MEDIA
|
||||
|
||||
void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid, const char *_trackID) {
|
||||
|
||||
optional<string> name = nullopt;
|
||||
if (_name) {
|
||||
name = string(_name);
|
||||
}
|
||||
|
||||
optional<string> msid = nullopt;
|
||||
if (_msid) {
|
||||
msid = string(_msid);
|
||||
}
|
||||
|
||||
optional<string> trackID = nullopt;
|
||||
if (_trackID) {
|
||||
trackID = string(_trackID);
|
||||
}
|
||||
|
||||
description->addSSRC(ssrc, name, msid, trackID);
|
||||
}
|
||||
|
||||
int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
|
||||
return wrap([&] {
|
||||
auto track = getTrack(tr);
|
||||
@ -630,6 +677,85 @@ int rtcSetNeedsToSendRtcpSr(int id) {
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetTrackPayloadTypesForCodec(int tr, const char * ccodec, int * buffer, int size) {
|
||||
return wrap([&] {
|
||||
auto track = getTrack(tr);
|
||||
auto codec = lowercased(string(ccodec));
|
||||
auto description = track->description();
|
||||
std::vector<int> payloadTypes{};
|
||||
payloadTypes.reserve(std::max(size, 0));
|
||||
for (auto it = description.beginMaps(); it != description.endMaps(); it++) {
|
||||
auto element = *it;
|
||||
if (lowercased(element.second.format) == codec) {
|
||||
payloadTypes.push_back(element.first);
|
||||
}
|
||||
}
|
||||
return copyAndReturn(payloadTypes, buffer, size);
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int count) {
|
||||
return wrap([&] {
|
||||
auto track = getTrack(tr);
|
||||
auto ssrcs = track->description().getSSRCs();
|
||||
return copyAndReturn(ssrcs, buffer, count);
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char * cname, int cnameSize) {
|
||||
return wrap([&] {
|
||||
auto track = getTrack(tr);
|
||||
auto description = track->description();
|
||||
auto optCName = description.getCNameForSsrc(ssrc);
|
||||
if (optCName.has_value()) {
|
||||
return copyAndReturn(optCName.value(), cname, cnameSize);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetSsrcsForType(const char * mediaType, const char * sdp, uint32_t * buffer, int bufferSize) {
|
||||
return wrap([&] {
|
||||
auto type = lowercased(string(mediaType));
|
||||
auto oldSDP = string(sdp);
|
||||
auto description = Description(oldSDP, "unspec");
|
||||
auto mediaCount = description.mediaCount();
|
||||
for (unsigned int i = 0; i < mediaCount; i++) {
|
||||
if (std::holds_alternative<Description::Media *>(description.media(i))) {
|
||||
auto media = std::get<Description::Media *>(description.media(i));
|
||||
auto currentMediaType = lowercased(media->type());
|
||||
if (currentMediaType == type) {
|
||||
auto ssrcs = media->getSSRCs();
|
||||
return copyAndReturn(ssrcs, buffer, bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
int rtcSetSsrcForType(const char * mediaType, const char * sdp, char * buffer, const int bufferSize,
|
||||
rtcSsrcForTypeInit * init) {
|
||||
return wrap([&] {
|
||||
auto type = lowercased(string(mediaType));
|
||||
auto prevSDP = string(sdp);
|
||||
auto description = Description(prevSDP, "unspec");
|
||||
auto mediaCount = description.mediaCount();
|
||||
for (unsigned int i = 0; i < mediaCount; i++) {
|
||||
if (std::holds_alternative<Description::Media *>(description.media(i))) {
|
||||
auto media = std::get<Description::Media *>(description.media(i));
|
||||
auto currentMediaType = lowercased(media->type());
|
||||
if (currentMediaType == type) {
|
||||
setSSRC(media, init->ssrc, init->name, init->msid, init->trackId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return copyAndReturn(string(description), buffer, bufferSize);
|
||||
});
|
||||
}
|
||||
|
||||
#endif // RTC_ENABLE_MEDIA
|
||||
#if RTC_ENABLE_WEBSOCKET
|
||||
|
||||
@ -1136,6 +1262,23 @@ int rtcSetSctpSettings(const rtcSctpSettings *settings) {
|
||||
|
||||
if (settings->delayedSackTimeMs > 0)
|
||||
s.delayedSackTime = std::chrono::milliseconds(settings->delayedSackTimeMs);
|
||||
else if (settings->delayedSackTimeMs < 0)
|
||||
s.delayedSackTime = std::chrono::milliseconds(0);
|
||||
|
||||
if (settings->minRetransmitTimeoutMs > 0)
|
||||
s.minRetransmitTimeout = std::chrono::milliseconds(settings->minRetransmitTimeoutMs);
|
||||
|
||||
if (settings->maxRetransmitTimeoutMs > 0)
|
||||
s.maxRetransmitTimeout = std::chrono::milliseconds(settings->maxRetransmitTimeoutMs);
|
||||
|
||||
if (settings->initialRetransmitTimeoutMs > 0)
|
||||
s.initialRetransmitTimeout = std::chrono::milliseconds(settings->initialRetransmitTimeoutMs);
|
||||
|
||||
if (settings->maxRetransmitAttempts > 0)
|
||||
s.maxRetransmitAttempts = settings->maxRetransmitAttempts;
|
||||
|
||||
if (settings->heartbeatIntervalMs > 0)
|
||||
s.heartbeatInterval = std::chrono::milliseconds(settings->heartbeatIntervalMs);
|
||||
|
||||
SetSctpSettings(std::move(s));
|
||||
return RTC_ERR_SUCCESS;
|
||||
|
@ -547,8 +547,11 @@ void Description::Entry::parseSdpLine(string_view line) {
|
||||
mAttributes.emplace_back(line.substr(2));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<string>::iterator Description::Entry::beginAttributes() { return mAttributes.begin(); }
|
||||
|
||||
std::vector<string>::iterator Description::Entry::endAttributes() { return mAttributes.end(); }
|
||||
|
||||
std::vector<string>::iterator
|
||||
Description::Entry::removeAttribute(std::vector<string>::iterator it) {
|
||||
return mAttributes.erase(it);
|
||||
@ -556,10 +559,12 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
|
||||
|
||||
void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid,
|
||||
optional<string> trackID) {
|
||||
if (name)
|
||||
if (name) {
|
||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
||||
else
|
||||
mCNameMap.emplace(ssrc, *name);
|
||||
} else {
|
||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
|
||||
}
|
||||
|
||||
if (msid)
|
||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " +
|
||||
@ -568,26 +573,28 @@ void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<
|
||||
mSsrcs.emplace_back(ssrc);
|
||||
}
|
||||
|
||||
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
||||
optional<string> msid, optional<string> trackID) {
|
||||
auto it = mAttributes.begin();
|
||||
while (it != mAttributes.end()) {
|
||||
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
||||
it = mAttributes.erase(it);
|
||||
} else
|
||||
it++;
|
||||
}
|
||||
addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
|
||||
}
|
||||
|
||||
void Description::Media::removeSSRC(uint32_t oldSSRC) {
|
||||
auto it = mAttributes.begin();
|
||||
while (it != mAttributes.end()) {
|
||||
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
||||
if (match_prefix(*it, "ssrc:" + std::to_string(oldSSRC)))
|
||||
it = mAttributes.erase(it);
|
||||
} else
|
||||
it++;
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
auto jt = mSsrcs.begin();
|
||||
while (jt != mSsrcs.end()) {
|
||||
if (*jt == oldSSRC)
|
||||
jt = mSsrcs.erase(jt);
|
||||
else
|
||||
++jt;
|
||||
}
|
||||
}
|
||||
|
||||
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
||||
optional<string> msid, optional<string> trackID) {
|
||||
removeSSRC(oldSSRC);
|
||||
addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
|
||||
}
|
||||
|
||||
bool Description::Media::hasSSRC(uint32_t ssrc) {
|
||||
@ -858,7 +865,16 @@ void Description::Media::parseSdpLine(string_view line) {
|
||||
} else if (key == "rtcp-mux") {
|
||||
// always added
|
||||
} else if (key == "ssrc") {
|
||||
mSsrcs.emplace_back(to_integer<uint32_t>(value));
|
||||
auto ssrc = to_integer<uint32_t>(value);
|
||||
if (!hasSSRC(ssrc)) {
|
||||
mSsrcs.emplace_back(ssrc);
|
||||
}
|
||||
auto cnamePos = value.find("cname:");
|
||||
if (cnamePos != string::npos) {
|
||||
auto cname = value.substr(cnamePos + 6);
|
||||
mCNameMap.emplace(ssrc, cname);
|
||||
}
|
||||
mAttributes.emplace_back(attr);
|
||||
} else {
|
||||
Entry::parseSdpLine(line);
|
||||
}
|
||||
@ -875,6 +891,14 @@ void Description::Media::addRTPMap(const Description::Media::RTPMap &map) {
|
||||
|
||||
std::vector<uint32_t> Description::Media::getSSRCs() { return mSsrcs; }
|
||||
|
||||
optional<string> Description::Media::getCNameForSsrc(uint32_t ssrc) {
|
||||
auto it = mCNameMap.find(ssrc);
|
||||
if (it != mCNameMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
|
||||
return mRtpMap.begin();
|
||||
}
|
||||
@ -910,11 +934,16 @@ int Description::Media::RTPMap::parsePT(string_view view) {
|
||||
|
||||
void Description::Media::RTPMap::setMLine(string_view mline) {
|
||||
size_t p = mline.find(' ');
|
||||
if (p == string::npos)
|
||||
throw std::invalid_argument("Invalid m-line");
|
||||
|
||||
this->pt = to_integer<int>(mline.substr(0, p));
|
||||
|
||||
string_view line = mline.substr(p + 1);
|
||||
size_t spl = line.find('/');
|
||||
if (spl == string::npos)
|
||||
throw std::invalid_argument("Invalid m-line");
|
||||
|
||||
this->format = line.substr(0, spl);
|
||||
|
||||
line = line.substr(spl + 1);
|
||||
@ -971,7 +1000,7 @@ string Description::typeToString(Type type) {
|
||||
} // namespace rtc
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
|
||||
return out << std::string(description);
|
||||
return out << string(description);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Type type) {
|
||||
@ -980,18 +1009,17 @@ std::ostream &operator<<(std::ostream &out, rtc::Description::Type type) {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Role role) {
|
||||
using Role = rtc::Description::Role;
|
||||
const char *str;
|
||||
// Used for SDP generation, do not change
|
||||
switch (role) {
|
||||
case Role::Active:
|
||||
str = "active";
|
||||
out << "active";
|
||||
break;
|
||||
case Role::Passive:
|
||||
str = "passive";
|
||||
out << "passive";
|
||||
break;
|
||||
default:
|
||||
str = "actpass";
|
||||
out << "actpass";
|
||||
break;
|
||||
}
|
||||
return out << str;
|
||||
return out;
|
||||
}
|
||||
|
@ -93,3 +93,31 @@ void Cleanup() { Init::Cleanup(); }
|
||||
void SetSctpSettings(SctpSettings s) { Init::SetSctpSettings(std::move(s)); }
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::LogLevel level) {
|
||||
switch (level) {
|
||||
case rtc::LogLevel::Fatal:
|
||||
out << "fatal";
|
||||
break;
|
||||
case rtc::LogLevel::Error:
|
||||
out << "error";
|
||||
break;
|
||||
case rtc::LogLevel::Warning:
|
||||
out << "warning";
|
||||
break;
|
||||
case rtc::LogLevel::Info:
|
||||
out << "info";
|
||||
break;
|
||||
case rtc::LogLevel::Debug:
|
||||
out << "debug";
|
||||
break;
|
||||
case rtc::LogLevel::Verbose:
|
||||
out << "verbose";
|
||||
break;
|
||||
default:
|
||||
out << "none";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "sctptransport.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace rtc::impl {
|
||||
|
||||
|
@ -131,6 +131,11 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
|
||||
jconfig.turn_servers = k > 0 ? turn_servers : nullptr;
|
||||
jconfig.turn_servers_count = k;
|
||||
|
||||
// Bind address
|
||||
if (config.bindAddress) {
|
||||
jconfig.bind_address = config.bindAddress->c_str();
|
||||
}
|
||||
|
||||
// Port range
|
||||
if (config.portRangeBegin > 1024 ||
|
||||
(config.portRangeEnd != 0 && config.portRangeEnd != 65535)) {
|
||||
|
@ -25,7 +25,9 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
// RFC 8831: SCTP MUST support performing Path MTU discovery without relying on ICMP or ICMPv6 as
|
||||
@ -114,14 +116,6 @@ void SctpTransport::Init() {
|
||||
usrsctp_init(0, &SctpTransport::WriteCallback, nullptr);
|
||||
usrsctp_sysctl_set_sctp_pr_enable(1); // Enable Partial Reliability Extension (RFC 3758)
|
||||
usrsctp_sysctl_set_sctp_ecn_enable(0); // Disable Explicit Congestion Notification
|
||||
usrsctp_sysctl_set_sctp_init_rtx_max_default(5);
|
||||
usrsctp_sysctl_set_sctp_path_rtx_max_default(5);
|
||||
usrsctp_sysctl_set_sctp_assoc_rtx_max_default(5); // single path
|
||||
usrsctp_sysctl_set_sctp_rto_min_default(1 * 1000); // ms
|
||||
usrsctp_sysctl_set_sctp_rto_max_default(10 * 1000); // ms
|
||||
usrsctp_sysctl_set_sctp_rto_initial_default(1 * 1000); // ms
|
||||
usrsctp_sysctl_set_sctp_init_rto_max_default(10 * 1000); // ms
|
||||
usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms
|
||||
}
|
||||
|
||||
void SctpTransport::SetSettings(const SctpSettings &s) {
|
||||
@ -144,9 +138,33 @@ void SctpTransport::SetSettings(const SctpSettings &s) {
|
||||
// See https://github.com/paullouisageneau/libdatachannel/issues/354
|
||||
usrsctp_sysctl_set_sctp_default_cc_module(to_uint32(s.congestionControlModule.value_or(0)));
|
||||
|
||||
// Reduce SACK delay to 20ms by default
|
||||
// Reduce SACK delay to 20ms by default (the recommended default value from RFC 4960 is 200ms)
|
||||
usrsctp_sysctl_set_sctp_delayed_sack_time_default(
|
||||
to_uint32(s.delayedSackTime.value_or(20ms).count()));
|
||||
|
||||
// RTO settings
|
||||
// RFC 2988 recommends a 1s min RTO, which is very high, but TCP on Linux has a 200ms min RTO
|
||||
usrsctp_sysctl_set_sctp_rto_min_default(
|
||||
to_uint32(s.minRetransmitTimeout.value_or(200ms).count()));
|
||||
// Set only 10s as max RTO instead of 60s for shorter connection timeout
|
||||
usrsctp_sysctl_set_sctp_rto_max_default(
|
||||
to_uint32(s.maxRetransmitTimeout.value_or(10000ms).count()));
|
||||
usrsctp_sysctl_set_sctp_init_rto_max_default(
|
||||
to_uint32(s.maxRetransmitTimeout.value_or(10000ms).count()));
|
||||
// Still set 1s as initial RTO
|
||||
usrsctp_sysctl_set_sctp_rto_initial_default(
|
||||
to_uint32(s.initialRetransmitTimeout.value_or(1000ms).count()));
|
||||
|
||||
// RTX settings
|
||||
// 5 retransmissions instead of 8 to shorten the backoff for shorter connection timeout
|
||||
auto maxRtx = to_uint32(s.maxRetransmitAttempts.value_or(5));
|
||||
usrsctp_sysctl_set_sctp_init_rtx_max_default(maxRtx);
|
||||
usrsctp_sysctl_set_sctp_assoc_rtx_max_default(maxRtx);
|
||||
usrsctp_sysctl_set_sctp_path_rtx_max_default(maxRtx); // single path
|
||||
|
||||
// Heartbeat interval
|
||||
usrsctp_sysctl_set_sctp_heartbeat_interval_default(
|
||||
to_uint32(s.heartbeatInterval.value_or(10000ms).count()));
|
||||
}
|
||||
|
||||
void SctpTransport::Cleanup() {
|
||||
@ -864,7 +882,7 @@ optional<milliseconds> SctpTransport::rtt() {
|
||||
void SctpTransport::UpcallCallback(struct socket *, void *arg, int /* flags */) {
|
||||
auto *transport = static_cast<SctpTransport *>(arg);
|
||||
|
||||
if(auto lock = Instances->lock(transport))
|
||||
if (auto lock = Instances->lock(transport))
|
||||
transport->handleUpcall();
|
||||
}
|
||||
|
||||
@ -873,7 +891,7 @@ int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos,
|
||||
|
||||
// Workaround for sctplab/usrsctp#405: Send callback is invoked on already closed socket
|
||||
// https://github.com/sctplab/usrsctp/issues/405
|
||||
if(auto lock = Instances->lock(transport))
|
||||
if (auto lock = Instances->lock(transport))
|
||||
return transport->handleWrite(static_cast<byte *>(data), len, tos, set_df);
|
||||
else
|
||||
return -1;
|
||||
|
@ -29,8 +29,6 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "usrsctp.h"
|
||||
|
||||
|
Reference in New Issue
Block a user