Browse Source

Merge pull request #6 from paullouisageneau/master

Sync forward
Erik Cota-Robles 4 years ago
parent
commit
1a8d8ec2f9

+ 7 - 1
CMakeLists.txt

@@ -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.0
+	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)
 
 

+ 1 - 0
README.md

@@ -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)
 
 

+ 1 - 1
deps/libjuice

@@ -1 +1 @@
-Subproject commit 45aafbc99fc2f95dc38c0abbfe588f2ba6711f6d
+Subproject commit 2111295fe8bb1c3f70b3c71a59cf0b33398a727d

+ 1 - 0
examples/client/CMakeLists.txt

@@ -5,6 +5,7 @@ endif()
 
 
 if(WIN32)
 if(WIN32)
 add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h getopt.cpp getopt.h)
 add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h getopt.cpp getopt.h)
+target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
 else()
 else()
 add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
 add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
 endif()
 endif()

+ 4 - 4
examples/client/getopt.cpp

@@ -84,7 +84,7 @@ static void exchange_a(char **argv, struct _getopt_data_a *d)
 		if (top - middle > middle - bottom)
 		if (top - middle > middle - bottom)
 		{
 		{
 			int len = middle - bottom;
 			int len = middle - bottom;
-			register int i;
+			int i;
 			for (i = 0; i < len; i++)
 			for (i = 0; i < len; i++)
 			{
 			{
 				tem = argv[bottom + i];
 				tem = argv[bottom + i];
@@ -96,7 +96,7 @@ static void exchange_a(char **argv, struct _getopt_data_a *d)
 		else
 		else
 		{
 		{
 			int len = top - middle;
 			int len = top - middle;
-			register int i;
+			int i;
 			for (i = 0; i < len; i++)
 			for (i = 0; i < len; i++)
 			{
 			{
 				tem = argv[bottom + i];
 				tem = argv[bottom + i];
@@ -544,7 +544,7 @@ static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
 		if (top - middle > middle - bottom)
 		if (top - middle > middle - bottom)
 		{
 		{
 			int len = middle - bottom;
 			int len = middle - bottom;
-			register int i;
+			int i;
 			for (i = 0; i < len; i++)
 			for (i = 0; i < len; i++)
 			{
 			{
 				tem = argv[bottom + i];
 				tem = argv[bottom + i];
@@ -556,7 +556,7 @@ static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
 		else
 		else
 		{
 		{
 			int len = top - middle;
 			int len = top - middle;
-			register int i;
+			int i;
 			for (i = 0; i < len; i++)
 			for (i = 0; i < len; i++)
 			{
 			{
 				tem = argv[bottom + i];
 				tem = argv[bottom + i];

+ 2 - 3
examples/client/main.cpp

@@ -49,8 +49,7 @@ bool echoDataChannelMessages = false;
 
 
 shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
 shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
                                                 weak_ptr<WebSocket> wws, string id);
                                                 weak_ptr<WebSocket> wws, string id);
-
-void confirmOnStdout(bool echoed, string id, string type, int length);
+void confirmOnStdout(bool echoed, string id, string type, size_t length);
 string randomId(size_t length);
 string randomId(size_t length);
 
 
 int main(int argc, char **argv) {
 int main(int argc, char **argv) {
@@ -259,7 +258,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
 	return pc;
 	return pc;
 };
 };
 
 
-void confirmOnStdout(bool echoed, string id, string type, int length) {
+void confirmOnStdout(bool echoed, string id, string type, size_t length) {
 	static long count = 0;
 	static long count = 0;
 	static long freq = 100;
 	static long freq = 100;
 	if (!(++count%freq)) {
 	if (!(++count%freq)) {

+ 1 - 1
examples/media/main.cpp

@@ -73,7 +73,7 @@ int main() {
 		track->onMessage(
 		track->onMessage(
 		    [session, sock, addr](rtc::binary message) {
 		    [session, sock, addr](rtc::binary message) {
 			    // This is an RTP packet
 			    // This is an RTP packet
-			    sendto(sock, reinterpret_cast<const char *>(message.data()), message.size(), 0,
+			    sendto(sock, reinterpret_cast<const char *>(message.data()), int(message.size()), 0,
 			           reinterpret_cast<const struct sockaddr *>(&addr), sizeof(addr));
 			           reinterpret_cast<const struct sockaddr *>(&addr), sizeof(addr));
 		    },
 		    },
 		    nullptr);
 		    nullptr);

+ 6 - 0
include/rtc/queue.hpp

@@ -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();

+ 24 - 14
include/rtc/rtc.h

@@ -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 (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
-typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
-typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
-typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
-typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
-typedef void (*rtcTrackCallbackFunc)(int tr, void *ptr);
-typedef void (*rtcOpenCallbackFunc)(void *ptr);
-typedef void (*rtcClosedCallbackFunc)(void *ptr);
-typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
-typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
-typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr);
-typedef void (*rtcAvailableCallbackFunc)(void *ptr);
+typedef void (RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
+typedef void (RTC_API *rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
+typedef void (RTC_API *rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
+typedef void (RTC_API *rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
+typedef void (RTC_API *rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
+typedef void (RTC_API *rtcDataChannelCallbackFunc)(int dc, void *ptr);
+typedef void (RTC_API *rtcTrackCallbackFunc)(int tr, void *ptr);
+typedef void (RTC_API *rtcOpenCallbackFunc)(void *ptr);
+typedef void (RTC_API *rtcClosedCallbackFunc)(void *ptr);
+typedef void (RTC_API *rtcErrorCallbackFunc)(const char *error, void *ptr);
+typedef void (RTC_API *rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
+typedef void (RTC_API *rtcBufferedAmountLowCallbackFunc)(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);
 
 

+ 4 - 3
src/candidate.cpp

@@ -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];
@@ -113,9 +113,10 @@ bool Candidate::resolve(ResolveMode mode) {
 						break;
 						break;
 					}
 					}
 				}
 				}
-		}
+			}
 
 
-		freeaddrinfo(result);
+			freeaddrinfo(result);
+		}
 	}
 	}
 
 
 	return mIsResolved;
 	return mIsResolved;

+ 73 - 12
src/capi.cpp

@@ -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) {
-		std::lock_guard lock(mutex);
+	plogAppender(plogAppender &&appender) : callback(nullptr) {
+		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;
-	if (appender)
-		appender->set_callback(cb);
-	else if (cb)
-		appender.emplace(plog_appender(cb));
-
-	InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr);
+	static std::optional<plogAppender> appender;
+	const auto severity = static_cast<plog::Severity>(level);
+	std::lock_guard lock(mutex);
+	if (appender) {
+		appender->setCallback(cb);
+		InitLogger(severity, nullptr); // change the severity
+	} 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;
 	});
 	});
 }
 }
 
 

+ 2 - 0
src/datachannel.cpp

@@ -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:

+ 2 - 3
src/dtlstransport.cpp

@@ -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))
-				break; // queue is stopped
+			mIncomingQueue.wait(duration);
 		}
 		}
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
 		PLOG_ERROR << "DTLS recv: " << e.what();
 		PLOG_ERROR << "DTLS recv: " << e.what();

+ 10 - 3
src/icetransport.cpp

@@ -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) {

+ 4 - 0
src/log.cpp

@@ -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";

+ 7 - 6
src/peerconnection.cpp

@@ -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));
-
-	auto iceTransport = std::atomic_load(&mIceTransport);
-	auto sctpTransport = std::atomic_load(&mSctpTransport);
-	if (!iceTransport || !sctpTransport)
-		return;
-
 	if (!channel) {
 	if (!channel) {
+		auto iceTransport = std::atomic_load(&mIceTransport);
+		auto sctpTransport = std::atomic_load(&mSctpTransport);
+		if (!iceTransport || !sctpTransport)
+			return;
+
 		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,

+ 28 - 28
src/rtcp.cpp

@@ -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 void setSenderSSRC(SSRC ssrc) { this->senderSSRC = htonl(ssrc); }
+	inline SSRC getSenderSSRC() const { return ntohl(senderSsrc); }
+	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);
-		setBitrate(numSSRC, bitrate);
+		senderSsrc = htonl(senderSsrc_);
+		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();
 
 

+ 1 - 3
src/sctptransport.cpp

@@ -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();
 			}
 			}
 		}
 		}

+ 35 - 16
src/websocket.cpp

@@ -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");
+
+	// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
+	static const char *rs =
+	    R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
 
 
-	static const char *rs = R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)";
-	static std::regex regex(rs, std::regex::extended);
+	static const std::regex r(rs, std::regex::extended);
 
 
-	std::smatch match;
-	if (!std::regex_match(url, match, regex))
-		throw std::invalid_argument("Malformed WebSocket URL: " + url);
+	std::smatch m;
+	if (!std::regex_match(url, m, r) || m[10].length() == 0)
+		throw std::invalid_argument("Invalid WebSocket URL: " + url);
 
 
-	mScheme = match[2];
-	if (mScheme != "ws" && mScheme != "wss")
+	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];
-	if (auto pos = mHost.find(':'); pos != string::npos) {
-		mHostname = mHost.substr(0, pos);
-		mService = mHost.substr(pos + 1);
-	} else {
-		mHostname = mHost;
+	mHostname = m[10];
+	mService = m[12];
+	if (mService.empty()) {
 		mService = mScheme == "ws" ? "80" : "443";
 		mService = mScheme == "ws" ? "80" : "443";
+		mHost = mHostname;
+	} else {
+		mHost = mHostname + ':' + mService;
 	}
 	}
 
 
-	mPath = match[5];
-	if (string query = match[7]; !query.empty())
+	while (!mHostname.empty() && mHostname.front() == '[')
+		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());

+ 7 - 1
src/wstransport.cpp

@@ -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());

+ 2 - 1
test/benchmark.cpp

@@ -155,7 +155,8 @@ size_t benchmark(milliseconds duration) {
 
 
 	endTime = steady_clock::now();
 	endTime = steady_clock::now();
 
 
-	auto connectDuration = duration_cast<milliseconds>(openTime - startTime);
+	auto connectDuration = duration_cast<milliseconds>(dc1->isOpen() ? openTime - startTime
+	                                                                 : steady_clock::duration(0));
 	auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
 	auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
 
 
 	cout << "Test duration: " << duration.count() << " ms" << endl;
 	cout << "Test duration: " << duration.count() << " ms" << endl;

+ 51 - 9
test/capi_connectivity.cpp

@@ -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,15 +185,55 @@ int test_capi_connectivity_main() {
 		goto error;
 		goto error;
 	}
 	}
 
 
-	char buffer[256];
-	if (rtcGetLocalAddress(peer1->pc, buffer, 256) >= 0)
-		printf("Local address 1:  %s\n", buffer);
-	if (rtcGetRemoteAddress(peer1->pc, buffer, 256) >= 0)
-		printf("Remote address 1: %s\n", buffer);
-	if (rtcGetLocalAddress(peer2->pc, buffer, 256) >= 0)
-		printf("Local address 2:  %s\n", buffer);
-	if (rtcGetRemoteAddress(peer2->pc, buffer, 256) >= 0)
-		printf("Remote address 2: %s\n", buffer);
+	char buffer[BUFFER_SIZE];
+
+	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);
+
+	if (rtcGetRemoteAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
+		fprintf(stderr, "rtcGetRemoteAddress failed\n");
+		goto error;
+	}
+	printf("Remote address 1: %s\n", buffer);
+
+	if (rtcGetLocalAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
+		fprintf(stderr, "rtcGetLocalAddress failed\n");
+		goto error;
+	}
+	printf("Local address 2:  %s\n", buffer);
+
+	if (rtcGetRemoteAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
+		fprintf(stderr, "rtcGetRemoteAddress failed\n");
+		goto error;
+	}
+	printf("Remote address 2: %s\n", buffer);
 
 
 	deletePeer(peer1);
 	deletePeer(peer1);
 	sleep(1);
 	sleep(1);

+ 44 - 0
test/connectivity.cpp

@@ -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);