Compare commits

...

49 Commits

Author SHA1 Message Date
8ea828f1b0 Bumped version to 0.9.4 2020-10-04 14:12:21 +02:00
35da6636aa Merge pull request #207 from paullouisageneau/capi-description
Improve consistency of C API
2020-10-03 22:11:15 +02:00
363827594b Added tests for rtcSetLocalDescription() and rtcSetRemoteDescription() 2020-10-03 21:57:31 +02:00
cbc027f144 Added rtcGetRemoteDescription() for the sake of completeness 2020-10-03 19:55:14 +02:00
ebc6a4b65c Renamed rtcGetLocalDescriptionSdp() to rtcGetLocalDescription() 2020-10-03 19:54:11 +02:00
37d47d28a8 Made rtcGet*Address() return RTC_ERR_FAILURE if unavailable 2020-10-03 19:51:06 +02:00
46878519c0 Merge pull request #205 from hanseuljun/rtcgetlocaldescriptionsdp
Add rtcGetLocalDescriptionSdp() as a C API
2020-10-03 19:48:58 +02:00
84c298f4f8 Add return RTC_ERR_FAILURE at the end of rtcGetLocalDescriptionSdp. 2020-10-03 09:57:21 -07:00
23aed2b844 Add rtcGetLocalDescription(). 2020-10-02 15:11:32 -07:00
df62d6d51c Merge pull request #201 from paullouisageneau/fix-rtcinitlogger
Fix rtcInitLogger to prevent logging multiple times
2020-10-02 13:35:02 +02:00
26241f00b7 Fixed case plog_appender -> plogAppender 2020-10-02 09:15:09 +02:00
873d14c824 Merge pull request #203 from hanseuljun/capi-stdcall
Add CAPI_STDCALL in cmake as an option
2020-10-02 09:14:46 +02:00
4953a112ad Reset callback on plog_appender move 2020-10-02 09:11:54 +02:00
c31e1bf0be Make addition of CAPI_STDCALL as definition per target. 2020-10-01 09:24:43 -07:00
98ddba192f Added move constructor to plog_appender 2020-10-01 13:31:25 +02:00
b02b30eea8 Make plog_appender use its own mutex 2020-10-01 13:13:39 +02:00
324d97a9b7 Merge pull request #202 from hanseuljun/fix-rtcinitlogger
A tiny modification to #201
2020-10-01 09:06:25 +02:00
0a1dd4db01 Add CAPI_STDCALL in cmake as an option and definition to set the calling convention of the C callback functions __stdcall. 2020-09-30 19:40:40 -07:00
b1de9acb20 Remove lock_guard inside rtcInitLogger since the same mutex gets locked inside appender->set_callback, causing a deadlock. 2020-09-30 17:47:50 -07:00
960300a7cd Updated libjuice to v0.5.2 2020-09-30 23:21:36 +02:00
3f084d7527 Added datachannel-unity 2020-09-30 23:13:36 +02:00
64096d599c Make rtcInitLogger and InitLogger thread-safe 2020-09-30 23:08:10 +02:00
552e443ef1 Fixed rtcInitLogger to prevent logging multiple times 2020-09-30 22:52:36 +02:00
52cb8d68a0 Updated libjuice 2020-09-29 21:47:11 +02:00
372e2b7a1f Merge pull request #199 from paullouisageneau/fix-websocket-url-parsing
Fix WebSocket URL parsing to handle user and password
2020-09-29 19:07:16 +02:00
a92e63720c Fixed WebSocket URL parsing to handle user and password 2020-09-29 12:42:41 +02:00
8d121c086e Merge pull request #196 from paullouisageneau/fix-null-logger
Fix null pointer access if the logger is not initialized
2020-09-29 00:13:24 +02:00
b538e454aa Fixed null pointer access if plog is not initialized 2020-09-28 23:54:32 +02:00
60d09d5c6f Bumped version to 0.9.3 2020-09-28 00:13:01 +02:00
266159fe41 Merge pull request #194 from paullouisageneau/fix-openssl-loop-quit
Fix OpenSSL recv loop quitting too early
2020-09-28 00:10:58 +02:00
458decb12d Fixed OpenSSL recv loop quitting too early 2020-09-27 23:53:35 +02:00
635c2e5513 Merge pull request #192 from paullouisageneau/fix-multiple-createdc
Fix exception on second call to createDataChannel()
2020-09-27 13:42:57 +02:00
adc4223617 Added a test creating a second data channel 2020-09-27 13:26:22 +02:00
326ae27ad1 Added missing return 2020-09-27 13:10:43 +02:00
6d33d19816 Merge pull request #191 from paullouisageneau/fix-message-size-checks
Clarify message size checks
2020-09-27 00:29:07 +02:00
734efb391a Removed misplaced len check 2020-09-27 00:21:36 +02:00
cba864507f Added control message size check when reading it 2020-09-27 00:21:18 +02:00
3432b233ff Updated libjuice to v0.5.1 2020-09-26 20:19:14 +02:00
ef9bfe811b Bumped version to 0.9.2 2020-09-26 18:28:22 +02:00
949e1de9cd Only retrieve pointers to transport if necessary when forwarding 2020-09-26 17:54:11 +02:00
57c52cf7ae Fixed compilation warnings 2020-09-26 17:24:44 +02:00
eaac06546e Merge pull request #189 from paullouisageneau/fix-websocket-incoming-null
Handle remote close on null message in websocket
2020-09-26 16:29:13 +02:00
9e38d08c0b Merge pull request #188 from paullouisageneau/fix-freeaddrinfo
Fix freeaddrinfo() called with NULL on getaddrinfo() failure
2020-09-26 16:25:04 +02:00
47db28617a Handle remote close on null message in websocket 2020-09-26 16:20:21 +02:00
de8c4a55cf Added some logging on address resolution failure 2020-09-26 16:13:12 +02:00
08d94e59c7 Call freeaddrinfo only if getaddrinfo succeeds 2020-09-26 16:08:27 +02:00
e8a6698abd Merge pull request #185 from ecotarobles/master
example/client has "-n" option to not use STUN server
2020-09-25 18:35:16 +02:00
7348b2b350 example/client has "-n" option to not use STUN server 2020-09-25 16:25:26 +03:00
a99efd27d2 Merge pull request #5 from paullouisageneau/master
Sync with master
2020-09-24 16:48:22 +03:00
22 changed files with 336 additions and 118 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
project(libdatachannel project(libdatachannel
DESCRIPTION "WebRTC Data Channels Library" DESCRIPTION "WebRTC Data Channels Library"
VERSION 0.9.1 VERSION 0.9.4
LANGUAGES CXX) LANGUAGES CXX)
# Options # Options
@ -11,6 +11,7 @@ option(NO_WEBSOCKET "Disable WebSocket support" OFF)
option(NO_EXAMPLES "Disable examples" OFF) option(NO_EXAMPLES "Disable examples" OFF)
option(NO_TESTS "Disable tests build" OFF) option(NO_TESTS "Disable tests build" OFF)
option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option(CAPI_STDCALL "Set calling convention of C API callbacks stdcall" OFF)
if(USE_NICE) if(USE_NICE)
option(USE_JUICE "Use libjuice" OFF) option(USE_JUICE "Use libjuice" OFF)
@ -217,6 +218,11 @@ else()
target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic) target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic)
endif() endif()
if(CAPI_STDCALL)
target_compile_definitions(datachannel PUBLIC CAPI_STDCALL)
target_compile_definitions(datachannel-static PUBLIC CAPI_STDCALL)
endif()
add_library(LibDataChannel::LibDataChannel ALIAS datachannel) add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static) add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)

View File

@ -202,5 +202,6 @@ ws->open("wss://my.websocket/service");
## External resources ## External resources
- Rust wrapper for libdatachannel: [datachannel-rs](https://github.com/lerouxrgd/datachannel-rs) - Rust wrapper for libdatachannel: [datachannel-rs](https://github.com/lerouxrgd/datachannel-rs)
- Node.js wrapper for libdatachannel: [node-datachannel](https://github.com/murat-dogan/node-datachannel) - Node.js wrapper for libdatachannel: [node-datachannel](https://github.com/murat-dogan/node-datachannel)
- Unity wrapper for Windows 10 and Hololens: [datachannel-unity](https://github.com/hanseuljun/datachannel-unity)
- WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm) - WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm)

2
deps/libjuice vendored

View File

@ -1,4 +0,0 @@
# getopt-for-windows
getopt.h and getopt.c is very often used in linux, to make it easy for windows user, two files were extracted from glibc. In order to make it works properly in windows, some modification was done and you may compare the change using original source files. Enjoy it!
Source: https://github.com/Chunde/getopt-for-windows. IMPORTANT: getopt.[ch] are likely not safe for Linux due to conflict with existing getopt.[ch]. They are thus NOT in CMakeFiles.txt and instead both files are #include only on Windows.

View File

@ -66,12 +66,16 @@ int main(int argc, char **argv) {
Configuration config; Configuration config;
string stunServer = ""; string stunServer = "";
if (params->noStun()) {
cout << "No stun server is configured. Only local hosts and public IP addresses suported." << endl;
} else {
if (params->stunServer().substr(0,5).compare("stun:") != 0) { if (params->stunServer().substr(0,5).compare("stun:") != 0) {
stunServer = "stun:"; stunServer = "stun:";
} }
stunServer += params->stunServer() + ":" + to_string(params->stunPort()); stunServer += params->stunServer() + ":" + to_string(params->stunPort());
cout << "Stun server is " << stunServer << endl; cout << "Stun server is " << stunServer << endl;
config.iceServers.emplace_back(stunServer); config.iceServers.emplace_back(stunServer);
}
localId = randomId(4); localId = randomId(4);
cout << "The local ID is: " << localId << endl; cout << "The local ID is: " << localId << endl;

View File

@ -43,6 +43,8 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
static struct option long_options[] = static struct option long_options[] =
{ {
{"echo", no_argument, NULL, 'e'},
{"noStun", no_argument, NULL, 'n'},
{"stunServer", required_argument, NULL, 's'}, {"stunServer", required_argument, NULL, 's'},
{"stunPort", required_argument, NULL, 't'}, {"stunPort", required_argument, NULL, 't'},
{"webSocketServer", required_argument, NULL, 'w'}, {"webSocketServer", required_argument, NULL, 'w'},
@ -55,19 +57,28 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
_program_name += argv[0]; _program_name += argv[0];
/* default values */ /* default values */
_e = false;
_n = false;
_s = "stun.l.google.com"; _s = "stun.l.google.com";
_t = 19302; _t = 19302;
_w = "localhost"; _w = "localhost";
_x = 8000; _x = 8000;
_e = false;
_h = false; _h = false;
_v = false; _v = false;
optind = 0; optind = 0;
while ((c = getopt_long (argc, argv, "s:t:w:x:ehv", long_options, &optind)) != - 1) while ((c = getopt_long (argc, argv, "s:t:w:x:enhv", long_options, &optind)) != - 1)
{ {
switch (c) switch (c)
{ {
case 'e':
_e = true;
break;
case 'n':
_n = true;
break;
case 's': case 's':
_s = optarg; _s = optarg;
break; break;
@ -108,10 +119,6 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
} }
break; break;
case 'e':
_e = true;
break;
case 'h': case 'h':
_h = true; _h = true;
this->usage (EXIT_SUCCESS); this->usage (EXIT_SUCCESS);
@ -146,10 +153,12 @@ void Cmdline::usage (int status)
else else
{ {
std::cout << "\ std::cout << "\
usage: " << _program_name << " [ -estwxhv ] \n\ usage: " << _program_name << " [ -enstwxhv ] \n\
libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\ libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\
[ -e ] [ --echo ] (type=FLAG)\n\ [ -e ] [ --echo ] (type=FLAG)\n\
Echo data channel messages back to sender rather than putting to stdout.\n\ Echo data channel messages back to sender rather than putting to stdout.\n\
[ -n ] [ --noStun ] (type=FLAG)\n\
Do NOT use a stun server (overrides -s and -t).\n\
[ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\ [ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\
Stun server URL or IP address.\n\ Stun server URL or IP address.\n\
[ -t ] [ --stunPort ] (type=INTEGER, range=0...65535, default=19302)\n\ [ -t ] [ --stunPort ] (type=INTEGER, range=0...65535, default=19302)\n\

View File

@ -34,11 +34,12 @@ class Cmdline
{ {
private: private:
/* parameters */ /* parameters */
bool _e;
bool _n;
std::string _s; std::string _s;
int _t; int _t;
std::string _w; std::string _w;
int _x; int _x;
bool _e;
bool _h; bool _h;
bool _v; bool _v;
@ -60,11 +61,12 @@ public:
/* return next (non-option) parameter */ /* return next (non-option) parameter */
int next_param () { return _optind; } int next_param () { return _optind; }
bool echoDataChannelMessages () const { return _e; }
bool noStun () const { return _n; }
std::string stunServer () const { return _s; } std::string stunServer () const { return _s; }
int stunPort () const { return _t; } int stunPort () const { return _t; }
std::string webSocketServer () const { return _w; } std::string webSocketServer () const { return _w; }
int webSocketPort () const { return _x; } int webSocketPort () const { return _x; }
bool echoDataChannelMessages () const { return _e; }
bool h () const { return _h; } bool h () const { return _h; }
bool v () const { return _v; } bool v () const { return _v; }
}; };

View File

@ -38,6 +38,7 @@ public:
~Queue(); ~Queue();
void stop(); void stop();
bool running() const;
bool empty() const; bool empty() const;
bool full() const; bool full() const;
size_t size() const; // elements size_t size() const; // elements
@ -80,6 +81,11 @@ template <typename T> void Queue<T>::stop() {
mPushCondition.notify_all(); mPushCondition.notify_all();
} }
template <typename T> bool Queue<T>::running() const {
std::lock_guard lock(mMutex);
return !mQueue.empty() || !mStopping;
}
template <typename T> bool Queue<T>::empty() const { template <typename T> bool Queue<T>::empty() const {
std::lock_guard lock(mMutex); std::lock_guard lock(mMutex);
return mQueue.empty(); return mQueue.empty();

View File

@ -29,6 +29,12 @@ extern "C" {
#define RTC_EXPORT #define RTC_EXPORT
#endif #endif
#ifdef CAPI_STDCALL
#define RTC_API __stdcall
#else
#define RTC_API
#endif
#ifndef RTC_ENABLE_WEBSOCKET #ifndef RTC_ENABLE_WEBSOCKET
#define RTC_ENABLE_WEBSOCKET 1 #define RTC_ENABLE_WEBSOCKET 1
#endif #endif
@ -81,22 +87,23 @@ typedef struct {
unsigned int maxRetransmits; // ignored if reliable unsigned int maxRetransmits; // ignored if reliable
} rtcReliability; } rtcReliability;
typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message); typedef void (RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr); typedef void (RTC_API *rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr); typedef void (RTC_API *rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr); typedef void (RTC_API *rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr); typedef void (RTC_API *rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr); typedef void (RTC_API *rtcDataChannelCallbackFunc)(int dc, void *ptr);
typedef void (*rtcTrackCallbackFunc)(int tr, void *ptr); typedef void (RTC_API *rtcTrackCallbackFunc)(int tr, void *ptr);
typedef void (*rtcOpenCallbackFunc)(void *ptr); typedef void (RTC_API *rtcOpenCallbackFunc)(void *ptr);
typedef void (*rtcClosedCallbackFunc)(void *ptr); typedef void (RTC_API *rtcClosedCallbackFunc)(void *ptr);
typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr); typedef void (RTC_API *rtcErrorCallbackFunc)(const char *error, void *ptr);
typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr); typedef void (RTC_API *rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr); typedef void (RTC_API *rtcBufferedAmountLowCallbackFunc)(void *ptr);
typedef void (*rtcAvailableCallbackFunc)(void *ptr); typedef void (RTC_API *rtcAvailableCallbackFunc)(void *ptr);
// Log // Log
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); // NULL cb to log to stdout // NULL cb on the first call will log to stdout
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb);
// User pointer // User pointer
RTC_EXPORT void rtcSetUserPointer(int id, void *ptr); RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
@ -114,6 +121,9 @@ RTC_EXPORT int rtcSetLocalDescription(int pc);
RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type); RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid); RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
RTC_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size); RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size); RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);

View File

@ -94,7 +94,7 @@ bool Candidate::resolve(ResolveMode mode) {
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
if (getaddrinfo(node.c_str(), service.c_str(), &hints, &result) == 0) { if (getaddrinfo(node.c_str(), service.c_str(), &hints, &result) == 0) {
for (auto p = result; p; p = p->ai_next) for (auto p = result; p; p = p->ai_next) {
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
// Rewrite the candidate // Rewrite the candidate
char nodebuffer[MAX_NUMERICNODE_LEN]; char nodebuffer[MAX_NUMERICNODE_LEN];
@ -117,6 +117,7 @@ bool Candidate::resolve(ResolveMode mode) {
freeaddrinfo(result); freeaddrinfo(result);
} }
}
return mIsResolved; return mIsResolved;
} }

View File

@ -195,12 +195,17 @@ template <typename F> int wrap(F func) {
return RTC_ERR_SUCCESS; \ return RTC_ERR_SUCCESS; \
}) })
class plog_appender : public plog::IAppender { class plogAppender : public plog::IAppender {
public: public:
plog_appender(rtcLogCallbackFunc cb = nullptr) { set_callback(cb); } plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
void set_callback(rtcLogCallbackFunc cb) { plogAppender(plogAppender &&appender) : callback(nullptr) {
std::lock_guard lock(mutex); std::lock_guard lock(appender.callbackMutex);
std::swap(appender.callback, callback);
}
void setCallback(rtcLogCallbackFunc cb) {
std::lock_guard lock(callbackMutex);
callback = cb; callback = cb;
} }
@ -215,7 +220,7 @@ public:
#else #else
std::string str = formatted; std::string str = formatted;
#endif #endif
std::lock_guard lock(mutex); std::lock_guard lock(callbackMutex);
if (callback) if (callback)
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str()); callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
else else
@ -224,18 +229,24 @@ public:
private: private:
rtcLogCallbackFunc callback; rtcLogCallbackFunc callback;
std::mutex callbackMutex;
}; };
} // namespace } // namespace
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) { void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
static std::optional<plog_appender> appender; static std::optional<plogAppender> appender;
if (appender) const auto severity = static_cast<plog::Severity>(level);
appender->set_callback(cb); std::lock_guard lock(mutex);
else if (cb) if (appender) {
appender.emplace(plog_appender(cb)); appender->setCallback(cb);
InitLogger(severity, nullptr); // change the severity
InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr); } else if (cb) {
appender.emplace(plogAppender(cb));
InitLogger(severity, &appender.value());
} else {
InitLogger(severity, nullptr); // log to stdout
}
} }
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); } void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
@ -518,6 +529,52 @@ int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
}); });
} }
int rtcGetLocalDescription(int pc, char *buffer, int size) {
return WRAP({
auto peerConnection = getPeerConnection(pc);
if (size <= 0)
return 0;
if (!buffer)
throw std::invalid_argument("Unexpected null pointer for buffer");
if (auto desc = peerConnection->localDescription()) {
auto sdp = string(*desc);
const char *data = sdp.data();
size = std::min(size - 1, int(sdp.size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0';
return size + 1;
}
return RTC_ERR_FAILURE;
});
}
int rtcGetRemoteDescription(int pc, char *buffer, int size) {
return WRAP({
auto peerConnection = getPeerConnection(pc);
if (size <= 0)
return 0;
if (!buffer)
throw std::invalid_argument("Unexpected null pointer for buffer");
if (auto desc = peerConnection->remoteDescription()) {
auto sdp = string(*desc);
const char *data = sdp.data();
size = std::min(size - 1, int(sdp.size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0';
return size + 1;
}
return RTC_ERR_FAILURE;
});
}
int rtcGetLocalAddress(int pc, char *buffer, int size) { int rtcGetLocalAddress(int pc, char *buffer, int size) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
@ -535,6 +592,8 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
buffer[size] = '\0'; buffer[size] = '\0';
return size + 1; return size + 1;
} }
return RTC_ERR_FAILURE;
}); });
} }
@ -555,6 +614,8 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
buffer[size] = '\0'; buffer[size] = '\0';
return int(size + 1); return int(size + 1);
} }
return RTC_ERR_FAILURE;
}); });
} }

View File

@ -218,6 +218,8 @@ void DataChannel::incoming(message_ptr message) {
switch (message->type) { switch (message->type) {
case Message::Control: { case Message::Control: {
if (message->size() == 0)
break; // Ignore
auto raw = reinterpret_cast<const uint8_t *>(message->data()); auto raw = reinterpret_cast<const uint8_t *>(message->data());
switch (raw[0]) { switch (raw[0]) {
case MESSAGE_OPEN: case MESSAGE_OPEN:

View File

@ -435,7 +435,7 @@ void DtlsTransport::runRecvLoop() {
const size_t bufferSize = maxMtu; const size_t bufferSize = maxMtu;
byte buffer[bufferSize]; byte buffer[bufferSize];
while (true) { while (mIncomingQueue.running()) {
// Process pending messages // Process pending messages
while (auto next = mIncomingQueue.tryPop()) { while (auto next = mIncomingQueue.tryPop()) {
message_ptr message = std::move(*next); message_ptr message = std::move(*next);
@ -492,8 +492,7 @@ void DtlsTransport::runRecvLoop() {
} }
} }
if (!mIncomingQueue.wait(duration)) mIncomingQueue.wait(duration);
break; // queue is stopped
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
PLOG_ERROR << "DTLS recv: " << e.what(); PLOG_ERROR << "DTLS recv: " << e.what();

View File

@ -60,7 +60,8 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
} }
juice_log_level_t level; juice_log_level_t level;
switch (plog::get()->getMaxSeverity()) { auto logger = plog::get();
switch (logger ? logger->getMaxSeverity() : plog::none) {
case plog::none: case plog::none:
level = JUICE_LOG_LEVEL_NONE; level = JUICE_LOG_LEVEL_NONE;
break; break;
@ -380,8 +381,11 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
hints.ai_protocol = IPPROTO_UDP; hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
<< server.service;
continue; continue;
}
for (auto p = result; p; p = p->ai_next) { for (auto p = result; p; p = p->ai_next) {
if (p->ai_family == AF_INET) { if (p->ai_family == AF_INET) {
@ -423,8 +427,11 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
<< server.service;
continue; continue;
}
for (auto p = result; p; p = p->ai_next) { for (auto p = result; p; p = p->ai_next) {
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {

View File

@ -24,6 +24,8 @@
#include "plog/Log.h" #include "plog/Log.h"
#include "plog/Logger.h" #include "plog/Logger.h"
#include <mutex>
namespace rtc { namespace rtc {
void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); } void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
@ -31,6 +33,8 @@ void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level))
void InitLogger(plog::Severity severity, plog::IAppender *appender) { void InitLogger(plog::Severity severity, plog::IAppender *appender) {
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender; static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
static plog::Logger<0> *logger = nullptr; static plog::Logger<0> *logger = nullptr;
static std::mutex mutex;
std::lock_guard lock(mutex);
if (!logger) { if (!logger) {
logger = &plog::init(severity, appender ? appender : &consoleAppender); logger = &plog::init(severity, appender ? appender : &consoleAppender);
PLOG_DEBUG << "Logger initialized"; PLOG_DEBUG << "Logger initialized";

View File

@ -102,6 +102,7 @@ void PeerConnection::setLocalDescription() {
if (std::atomic_load(&mIceTransport)) { if (std::atomic_load(&mIceTransport)) {
PLOG_DEBUG << "Local description is already set, ignoring"; PLOG_DEBUG << "Local description is already set, ignoring";
return;
} }
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of // RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
@ -546,17 +547,17 @@ void PeerConnection::forwardMessage(message_ptr message) {
} }
auto channel = findDataChannel(uint16_t(message->stream)); auto channel = findDataChannel(uint16_t(message->stream));
if (!channel) {
auto iceTransport = std::atomic_load(&mIceTransport); auto iceTransport = std::atomic_load(&mIceTransport);
auto sctpTransport = std::atomic_load(&mSctpTransport); auto sctpTransport = std::atomic_load(&mSctpTransport);
if (!iceTransport || !sctpTransport) if (!iceTransport || !sctpTransport)
return; return;
if (!channel) {
const byte dataChannelOpenMessage{0x03}; const byte dataChannelOpenMessage{0x03};
unsigned int remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0; unsigned int remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
if (message->type == Message::Control && *message->data() == dataChannelOpenMessage && if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
message->stream % 2 == remoteParity) { message->stream % 2 == remoteParity) {
channel = channel =
std::make_shared<DataChannel>(shared_from_this(), sctpTransport, message->stream); std::make_shared<DataChannel>(shared_from_this(), sctpTransport, message->stream);
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this, channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,

View File

@ -71,24 +71,24 @@ private:
uint32_t _delaySinceLastReport; uint32_t _delaySinceLastReport;
public: public:
inline void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost, inline void preparePacket(SSRC ssrc_, [[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo, [[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP, uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
uint64_t lastSR_DELAY) { uint64_t lastSR_DELAY) {
setSeqNo(highestSeqNo, seqNoCycles); setSeqNo(highestSeqNo, seqNoCycles);
setJitter(jitter); setJitter(jitter);
setSSRC(ssrc); setSSRC(ssrc_);
// Middle 32 bits of NTP Timestamp // Middle 32 bits of NTP Timestamp
// this->lastReport = lastSR_NTP >> 16u; // _lastReport = lastSR_NTP >> 16u;
setNTPOfSR(uint32_t(lastSR_NTP)); setNTPOfSR(uint32_t(lastSR_NTP));
setDelaySinceSR(uint32_t(lastSR_DELAY)); setDelaySinceSR(uint32_t(lastSR_DELAY));
// The delay, expressed in units of 1/65536 seconds // The delay, expressed in units of 1/65536 seconds
// this->delaySinceLastReport = lastSR_DELAY; // _delaySinceLastReport = lastSR_DELAY;
} }
inline void setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); } inline void setSSRC(SSRC ssrc_) { ssrc = htonl(ssrc_); }
inline SSRC getSSRC() const { return ntohl(ssrc); } inline SSRC getSSRC() const { return ntohl(ssrc); }
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost, inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
@ -172,7 +172,7 @@ public:
struct RTCP_SR { struct RTCP_SR {
RTCP_HEADER header; RTCP_HEADER header;
SSRC senderSSRC; SSRC senderSsrc;
private: private:
uint64_t _ntpTimestamp; uint64_t _ntpTimestamp;
@ -183,11 +183,11 @@ private:
RTCP_ReportBlock _reportBlocks; RTCP_ReportBlock _reportBlocks;
public: public:
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) { inline void preparePacket(SSRC senderSsrc_, uint8_t reportCount) {
unsigned int length = unsigned int length =
((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1; ((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
header.prepareHeader(200, reportCount, uint16_t(length)); header.prepareHeader(200, reportCount, uint16_t(length));
this->senderSSRC = htonl(senderSSRC); senderSsrc = htonl(senderSsrc_);
} }
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; } inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
@ -209,7 +209,7 @@ public:
inline void log() const { inline void log() const {
header.log(); header.log();
PLOG_DEBUG << "RTCP SR: " PLOG_DEBUG << "RTCP SR: "
<< " SSRC=" << ntohl(senderSSRC) << ", NTP_TS=" << ntpTimestamp() << " SSRC=" << ntohl(senderSsrc) << ", NTP_TS=" << ntpTimestamp()
<< ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount() << ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
<< ", octetCount=" << octetCount(); << ", octetCount=" << octetCount();
@ -221,7 +221,7 @@ public:
struct RTCP_RR { struct RTCP_RR {
RTCP_HEADER header; RTCP_HEADER header;
SSRC senderSSRC; SSRC senderSsrc;
private: private:
RTCP_ReportBlock _reportBlocks; RTCP_ReportBlock _reportBlocks;
@ -230,19 +230,19 @@ public:
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; } inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; } inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
inline SSRC getSenderSSRC() const { return ntohl(senderSSRC); } inline SSRC getSenderSSRC() const { return ntohl(senderSsrc); }
inline void setSenderSSRC(SSRC ssrc) { this->senderSSRC = htonl(ssrc); } inline void setSenderSSRC(SSRC ssrc) { senderSsrc = htonl(ssrc); }
[[nodiscard]] inline size_t getSize() const { [[nodiscard]] inline size_t getSize() const {
// "length" in packet is one less than the number of 32 bit words in the packet. // "length" in packet is one less than the number of 32 bit words in the packet.
return sizeof(uint32_t) * (1 + size_t(header.length())); return sizeof(uint32_t) * (1 + size_t(header.length()));
} }
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) { inline void preparePacket(SSRC ssrc, uint8_t reportCount) {
// "length" in packet is one less than the number of 32 bit words in the packet. // "length" in packet is one less than the number of 32 bit words in the packet.
size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1; size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1;
header.prepareHeader(201, reportCount, uint16_t(length)); header.prepareHeader(201, reportCount, uint16_t(length));
this->senderSSRC = htonl(senderSSRC); senderSsrc = htonl(ssrc);
} }
inline static size_t sizeWithReportBlocks(uint8_t reportCount) { inline static size_t sizeWithReportBlocks(uint8_t reportCount) {
@ -252,7 +252,7 @@ public:
inline void log() const { inline void log() const {
header.log(); header.log();
PLOG_DEBUG << "RTCP RR: " PLOG_DEBUG << "RTCP RR: "
<< " SSRC=" << ntohl(senderSSRC); << " SSRC=" << ntohl(senderSsrc);
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) { for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
getReportBlock(i)->log(); getReportBlock(i)->log();
@ -262,7 +262,7 @@ public:
struct RTCP_REMB { struct RTCP_REMB {
RTCP_HEADER header; RTCP_HEADER header;
SSRC senderSSRC; SSRC senderSsrc;
SSRC mediaSourceSSRC; SSRC mediaSourceSSRC;
// Unique identifier // Unique identifier
@ -278,48 +278,48 @@ struct RTCP_REMB {
return sizeof(uint32_t) * (1 + size_t(header.length())); return sizeof(uint32_t) * (1 + size_t(header.length()));
} }
inline void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) { inline void preparePacket(SSRC senderSsrc_, unsigned int numSSRC, unsigned int br) {
// Report Count becomes the format here. // Report Count becomes the format here.
header.prepareHeader(206, 15, 0); header.prepareHeader(206, 15, 0);
// Always zero. // Always zero.
mediaSourceSSRC = 0; mediaSourceSSRC = 0;
this->senderSSRC = htonl(senderSSRC); senderSsrc = htonl(senderSsrc_);
setBitrate(numSSRC, bitrate); setBitrate(numSSRC, br);
} }
inline void setBitrate(unsigned int numSSRC, unsigned int bitrate) { inline void setBitrate(unsigned int numSSRC, unsigned int br) {
unsigned int exp = 0; unsigned int exp = 0;
while (bitrate > pow(2, 18) - 1) { while (br > pow(2, 18) - 1) {
exp++; exp++;
bitrate /= 2; br /= 2;
} }
// "length" in packet is one less than the number of 32 bit words in the packet. // "length" in packet is one less than the number of 32 bit words in the packet.
header.setLength(uint16_t(((sizeof(header) + 4 * 2 + 4 + 4) / 4) - 1 + numSSRC)); header.setLength(uint16_t(((sizeof(header) + 4 * 2 + 4 + 4) / 4) - 1 + numSSRC));
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate); bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | br);
} }
// TODO Make this work // TODO Make this work
// uint64_t getBitrate() const{ // uint64_t getBitrate() const{
// uint32_t ntohed = ntohl(this->bitrate); // uint32_t ntohed = ntohl(bitrate);
// uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1); // uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1);
// unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u)); // unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u));
// return bitrate * pow(2,exp); // return bitrate * pow(2,exp);
// } // }
// //
// uint8_t getNumSSRCS() const { // uint8_t getNumSSRCS() const {
// return ntohl(this->bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u)); // return ntohl(bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
// } // }
inline void setSSRC(uint8_t iterator, SSRC ssrc) { this->ssrc[iterator] = htonl(ssrc); } inline void setSSRC(uint8_t iterator, SSRC ssrc_) { ssrc[iterator] = htonl(ssrc_); }
inline void log() const { inline void log() const {
header.log(); header.log();
PLOG_DEBUG << "RTCP REMB: " PLOG_DEBUG << "RTCP REMB: "
<< " SSRC=" << ntohl(senderSSRC); << " SSRC=" << ntohl(senderSsrc);
} }
static unsigned int sizeWithSSRCs(int numSSRC) { static unsigned int sizeWithSSRCs(int numSSRC) {
@ -407,7 +407,7 @@ void RtcpSession::pushRR(unsigned int lastSR_delay) {
auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control); auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
auto rr = reinterpret_cast<RTCP_RR *>(msg->data()); auto rr = reinterpret_cast<RTCP_RR *>(msg->data());
rr->preparePacket(mSsrc, 1); rr->preparePacket(mSsrc, 1);
rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, mGreatestSeqNo, 0, 0, mSyncNTPTS, rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, uint16_t(mGreatestSeqNo), 0, 0, mSyncNTPTS,
lastSR_delay); lastSR_delay);
rr->log(); rr->log();

View File

@ -476,8 +476,6 @@ int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*a
const byte *data, size_t len, struct sctp_rcvinfo info, int flags) { const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
try { try {
PLOG_VERBOSE << "Handle recv, len=" << len; PLOG_VERBOSE << "Handle recv, len=" << len;
if (!len)
return 0; // Ignore
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB, // SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
// therefore partial notifications and messages need to be handled separately. // therefore partial notifications and messages need to be handled separately.
@ -497,7 +495,7 @@ int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*a
if (flags & MSG_EOR) { if (flags & MSG_EOR) {
// Message is complete, process it // Message is complete, process it
processData(std::move(mPartialMessage), info.rcv_sid, processData(std::move(mPartialMessage), info.rcv_sid,
PayloadId(htonl(info.rcv_ppid))); PayloadId(ntohl(info.rcv_ppid)));
mPartialMessage.clear(); mPartialMessage.clear();
} }
} }

View File

@ -51,31 +51,45 @@ WebSocket::~WebSocket() {
WebSocket::State WebSocket::readyState() const { return mState; } WebSocket::State WebSocket::readyState() const { return mState; }
void WebSocket::open(const string &url) { void WebSocket::open(const string &url) {
PLOG_VERBOSE << "Opening WebSocket to URL: " << url;
if (mState != State::Closed) if (mState != State::Closed)
throw std::runtime_error("WebSocket must be closed before opening"); throw std::logic_error("WebSocket must be closed before opening");
static const char *rs = R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)"; // Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
static std::regex regex(rs, std::regex::extended); static const char *rs =
R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
std::smatch match; static const std::regex r(rs, std::regex::extended);
if (!std::regex_match(url, match, regex))
throw std::invalid_argument("Malformed WebSocket URL: " + url);
mScheme = match[2]; std::smatch m;
if (mScheme != "ws" && mScheme != "wss") if (!std::regex_match(url, m, r) || m[10].length() == 0)
throw std::invalid_argument("Invalid WebSocket URL: " + url);
mScheme = m[2];
if (mScheme.empty())
mScheme = "ws";
else if (mScheme != "ws" && mScheme != "wss")
throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme); throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme);
mHost = match[4]; mHostname = m[10];
if (auto pos = mHost.find(':'); pos != string::npos) { mService = m[12];
mHostname = mHost.substr(0, pos); if (mService.empty()) {
mService = mHost.substr(pos + 1);
} else {
mHostname = mHost;
mService = mScheme == "ws" ? "80" : "443"; mService = mScheme == "ws" ? "80" : "443";
mHost = mHostname;
} else {
mHost = mHostname + ':' + mService;
} }
mPath = match[5]; while (!mHostname.empty() && mHostname.front() == '[')
if (string query = match[7]; !query.empty()) mHostname.erase(mHostname.begin());
while (!mHostname.empty() && mHostname.back() == ']')
mHostname.pop_back();
mPath = m[13];
if (mPath.empty())
mPath += '/';
if (string query = m[15]; !query.empty())
mPath += "?" + query; mPath += "?" + query;
changeState(State::Connecting); changeState(State::Connecting);
@ -133,6 +147,11 @@ bool WebSocket::outgoing(message_ptr message) {
} }
void WebSocket::incoming(message_ptr message) { void WebSocket::incoming(message_ptr message) {
if (!message) {
remoteClose();
return;
}
if (message->type == Message::String || message->type == Message::Binary) { if (message->type == Message::String || message->type == Message::Binary) {
mRecvQueue.push(message); mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size()); triggerAvailable(mRecvQueue.size());

View File

@ -58,6 +58,12 @@ WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string p
onRecv(recvCallback); onRecv(recvCallback);
PLOG_DEBUG << "Initializing WebSocket transport"; PLOG_DEBUG << "Initializing WebSocket transport";
if (mHost.empty())
throw std::invalid_argument("WebSocket HTTP host cannot be empty");
if (mPath.empty())
throw std::invalid_argument("WebSocket HTTP path cannot be empty");
} }
WsTransport::~WsTransport() { stop(); } WsTransport::~WsTransport() { stop(); }
@ -147,7 +153,7 @@ void WsTransport::close() {
} }
bool WsTransport::sendHttpRequest() { bool WsTransport::sendHttpRequest() {
PLOG_DEBUG << "Sending WebSocket HTTP request"; PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mPath;
changeState(State::Connecting); changeState(State::Connecting);
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count()); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());

View File

@ -29,6 +29,8 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#include <unistd.h> // for sleep #include <unistd.h> // for sleep
#endif #endif
#define BUFFER_SIZE 4096
typedef struct { typedef struct {
rtcState state; rtcState state;
rtcGatheringState gatheringState; rtcGatheringState gatheringState;
@ -183,14 +185,54 @@ int test_capi_connectivity_main() {
goto error; goto error;
} }
char buffer[256]; char buffer[BUFFER_SIZE];
if (rtcGetLocalAddress(peer1->pc, buffer, 256) >= 0)
if (rtcGetLocalDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 1: %s\n", buffer);
if (rtcGetRemoteDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescription failed\n");
goto error;
}
printf("Remote description 1: %s\n", buffer);
if (rtcGetLocalDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 2: %s\n", buffer);
if (rtcGetRemoteDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescription failed\n");
goto error;
}
printf("Remote description 2: %s\n", buffer);
if (rtcGetLocalAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalAddress failed\n");
goto error;
}
printf("Local address 1: %s\n", buffer); printf("Local address 1: %s\n", buffer);
if (rtcGetRemoteAddress(peer1->pc, buffer, 256) >= 0)
if (rtcGetRemoteAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteAddress failed\n");
goto error;
}
printf("Remote address 1: %s\n", buffer); printf("Remote address 1: %s\n", buffer);
if (rtcGetLocalAddress(peer2->pc, buffer, 256) >= 0)
if (rtcGetLocalAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalAddress failed\n");
goto error;
}
printf("Local address 2: %s\n", buffer); printf("Local address 2: %s\n", buffer);
if (rtcGetRemoteAddress(peer2->pc, buffer, 256) >= 0)
if (rtcGetRemoteAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteAddress failed\n");
goto error;
}
printf("Remote address 2: %s\n", buffer); printf("Remote address 2: %s\n", buffer);
deletePeer(peer1); deletePeer(peer1);

View File

@ -125,6 +125,7 @@ void test_connectivity() {
} }
}); });
// Wait a bit
int attempts = 10; int attempts = 10;
shared_ptr<DataChannel> adc2; shared_ptr<DataChannel> adc2;
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--) while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
@ -146,6 +147,49 @@ void test_connectivity() {
if (auto addr = pc2->remoteAddress()) if (auto addr = pc2->remoteAddress())
cout << "Remote address 2: " << *addr << endl; cout << "Remote address 2: " << *addr << endl;
// Try to open a second data channel with another label
shared_ptr<DataChannel> second2;
pc2->onDataChannel([&second2](shared_ptr<DataChannel> dc) {
cout << "Second DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
if (dc->label() != "second") {
cerr << "Wrong second DataChannel label" << endl;
return;
}
dc->onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) {
cout << "Second Message 2: " << get<string>(message) << endl;
}
});
dc->send("Send hello from 2");
std::atomic_store(&second2, dc);
});
auto second1 = pc1->createDataChannel("second");
second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() {
auto second1 = wsecond1.lock();
if (!second1)
return;
cout << "Second DataChannel 1: Open" << endl;
second1->send("Second hello from 1");
});
dc1->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) {
cout << "Second Message 1: " << get<string>(message) << endl;
}
});
// Wait a bit
attempts = 10;
shared_ptr<DataChannel> asecond2;
while (
(!(asecond2 = std::atomic_load(&second2)) || !asecond2->isOpen() || !second1->isOpen()) &&
attempts--)
this_thread::sleep_for(1s);
// Delay close of peer 2 to check closing works properly // Delay close of peer 2 to check closing works properly
pc1->close(); pc1->close();
this_thread::sleep_for(1s); this_thread::sleep_for(1s);