Paul-Louis Ageneau 5 лет назад
Родитель
Сommit
be04d8037e
9 измененных файлов с 317 добавлено и 49 удалено
  1. 1 0
      CMakeLists.txt
  2. 6 3
      Makefile
  3. 1 0
      include/rtc/include.hpp
  4. 8 1
      include/rtc/rtc.h
  5. 0 2
      src/channel.cpp
  6. 106 35
      src/rtc.cpp
  7. 178 0
      test/capi.cpp
  8. 5 6
      test/connectivity.cpp
  9. 12 2
      test/main.cpp

+ 1 - 0
CMakeLists.txt

@@ -39,6 +39,7 @@ set(LIBDATACHANNEL_SOURCES
 set(TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/test/capi.cpp
 )
 
 set(TESTS_OFFERER_SOURCES

+ 6 - 3
Makefile

@@ -6,7 +6,7 @@ AR=$(CROSS)ar
 RM=rm -f
 CXXFLAGS=-std=c++17
 CPPFLAGS=-O2 -pthread -fPIC -Wall -Wno-address-of-packed-member
-LDFLAGS=-pthread
+LDFLAGS=-pthread -g
 LIBS=
 LOCALLIBS=libusrsctp.a
 USRSCTP_DIR=deps/usrsctp
@@ -44,6 +44,9 @@ LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS))
 SRCS=$(shell printf "%s " src/*.cpp)
 OBJS=$(subst .cpp,.o,$(SRCS))
 
+TEST_SRCS=$(shell printf "%s " test/*.cpp)
+TEST_OBJS=$(subst .cpp,.o,$(TEST_SRCS))
+
 all: $(NAME).a $(NAME).so tests
 
 src/%.o: src/%.cpp
@@ -60,8 +63,8 @@ $(NAME).a: $(OBJS)
 $(NAME).so: $(LOCALLIBS) $(OBJS)
 	$(CXX) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDLIBS)
 
-tests: $(NAME).a test/main.o
-	$(CXX) $(LDFLAGS) -o $@ test/main.o $(NAME).a $(LDLIBS)
+tests: $(NAME).a $(TEST_OBJS)
+	$(CXX) $(LDFLAGS) -o $@ $(TEST_OBJS) $(NAME).a $(LDLIBS)
 
 clean:
 	-$(RM) include/rtc/*.d *.d

+ 1 - 0
include/rtc/include.hpp

@@ -64,6 +64,7 @@ template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
 template <typename... P> class synchronized_callback {
 public:
 	synchronized_callback() = default;
+	synchronized_callback(std::function<void(P...)> func) { *this = std::move(func); };
 	~synchronized_callback() { *this = nullptr; }
 
 	synchronized_callback &operator=(std::function<void(P...)> func) {

+ 8 - 1
include/rtc/rtc.h

@@ -52,12 +52,18 @@ typedef enum {
 	RTC_LOG_VERBOSE = 6
 } rtcLogLevel;
 
+typedef struct {
+	const char **iceServers;
+	int iceServersCount;
+} rtcConfiguration;
+
 typedef void (*dataChannelCallbackFunc)(int dc, void *ptr);
 typedef void (*descriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
 typedef void (*candidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
 typedef void (*stateChangeCallbackFunc)(rtcState state, void *ptr);
 typedef void (*gatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
 typedef void (*openCallbackFunc)(void *ptr);
+typedef void (*closedCallbackFunc)(void *ptr);
 typedef void (*errorCallbackFunc)(const char *error, void *ptr);
 typedef void (*messageCallbackFunc)(const char *message, int size, void *ptr);
 typedef void (*bufferedAmountLowCallbackFunc)(void *ptr);
@@ -70,7 +76,7 @@ void rtcInitLogger(rtcLogLevel level);
 void rtcSetUserPointer(int i, void *ptr);
 
 // PeerConnection
-int rtcCreatePeerConnection(const char **iceServers, int iceServersCount);
+int rtcCreatePeerConnection(const rtcConfiguration *config);
 int rtcDeletePeerConnection(int pc);
 
 int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb);
@@ -88,6 +94,7 @@ int rtcDeleteDataChannel(int dc);
 
 int rtcGetDataChannelLabel(int dc, char *buffer, int size);
 int rtcSetOpenCallback(int dc, openCallbackFunc cb);
+int rtcSetClosedCallback(int dc, closedCallbackFunc cb);
 int rtcSetErrorCallback(int dc, errorCallbackFunc cb);
 int rtcSetMessageCallback(int dc, messageCallbackFunc cb);
 int rtcSendMessage(int dc, const char *data, int size);

+ 0 - 2
src/channel.cpp

@@ -18,8 +18,6 @@
 
 #include "channel.hpp"
 
-namespace {}
-
 namespace rtc {
 
 size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }

+ 106 - 35
src/rtc.cpp

@@ -106,15 +106,28 @@ void rtcSetUserPointer(int i, void *ptr) {
 		userPointerMap.erase(i);
 }
 
-int rtcCreatePeerConnection(const char **iceServers, int iceServersCount) {
-	Configuration config;
-	for (int i = 0; i < iceServersCount; ++i)
-		config.iceServers.emplace_back(IceServer(string(iceServers[i])));
+int rtcCreatePeerConnection(const rtcConfiguration *config) {
+	Configuration c;
+	for (int i = 0; i < config->iceServersCount; ++i)
+		c.iceServers.emplace_back(string(config->iceServers[i]));
 
-	return emplacePeerConnection(std::make_shared<PeerConnection>(config));
+	return emplacePeerConnection(std::make_shared<PeerConnection>(c));
 }
 
-int rtcDeletePeerConnection(int pc) { return erasePeerConnection(pc) ? 0 : -1; }
+int rtcDeletePeerConnection(int pc) {
+	auto peerConnection = getPeerConnection(pc);
+	if (!peerConnection)
+		return -1;
+
+	peerConnection->onDataChannel(nullptr);
+	peerConnection->onLocalDescription(nullptr);
+	peerConnection->onLocalCandidate(nullptr);
+	peerConnection->onStateChange(nullptr);
+	peerConnection->onGatheringStateChange(nullptr);
+
+	erasePeerConnection(pc);
+	return 0;
+}
 
 int rtcCreateDataChannel(int pc, const char *label) {
 	auto peerConnection = getPeerConnection(pc);
@@ -127,19 +140,36 @@ int rtcCreateDataChannel(int pc, const char *label) {
 	return dc;
 }
 
-int rtcDeleteDataChannel(int dc) { return eraseDataChannel(dc) ? 0 : -1; }
+int rtcDeleteDataChannel(int dc) {
+	auto dataChannel = getDataChannel(dc);
+	if (!dataChannel)
+		return -1;
+
+	dataChannel->onOpen(nullptr);
+	dataChannel->onClosed(nullptr);
+	dataChannel->onError(nullptr);
+	dataChannel->onMessage(nullptr);
+	dataChannel->onBufferedAmountLow(nullptr);
+	dataChannel->onAvailable(nullptr);
+
+	eraseDataChannel(dc);
+	return 0;
+}
 
 int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
 	auto peerConnection = getPeerConnection(pc);
 	if (!peerConnection)
 		return -1;
 
-	peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
-		int dc = emplaceDataChannel(dataChannel);
-		void *ptr = getUserPointer(pc);
-		rtcSetUserPointer(dc, ptr);
-		cb(dc, ptr);
-	});
+	if (cb)
+		peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
+			int dc = emplaceDataChannel(dataChannel);
+			void *ptr = getUserPointer(pc);
+			rtcSetUserPointer(dc, ptr);
+			cb(dc, ptr);
+		});
+	else
+		peerConnection->onDataChannel(nullptr);
 	return 0;
 }
 
@@ -148,9 +178,12 @@ int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
 	if (!peerConnection)
 		return -1;
 
-	peerConnection->onLocalDescription([pc, cb](const Description &desc) {
-		cb(string(desc).c_str(), desc.typeString().c_str(), getUserPointer(pc));
-	});
+	if (cb)
+		peerConnection->onLocalDescription([pc, cb](const Description &desc) {
+			cb(string(desc).c_str(), desc.typeString().c_str(), getUserPointer(pc));
+		});
+	else
+		peerConnection->onLocalDescription(nullptr);
 	return 0;
 }
 
@@ -159,9 +192,12 @@ int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
 	if (!peerConnection)
 		return -1;
 
-	peerConnection->onLocalCandidate([pc, cb](const Candidate &cand) {
-		cb(cand.candidate().c_str(), cand.mid().c_str(), getUserPointer(pc));
-	});
+	if (cb)
+		peerConnection->onLocalCandidate([pc, cb](const Candidate &cand) {
+			cb(cand.candidate().c_str(), cand.mid().c_str(), getUserPointer(pc));
+		});
+	else
+		peerConnection->onLocalCandidate(nullptr);
 	return 0;
 }
 
@@ -170,9 +206,12 @@ int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
 	if (!peerConnection)
 		return -1;
 
-	peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
-		cb(static_cast<rtcState>(state), getUserPointer(pc));
-	});
+	if (cb)
+		peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
+			cb(static_cast<rtcState>(state), getUserPointer(pc));
+		});
+	else
+		peerConnection->onStateChange(nullptr);
 	return 0;
 }
 
@@ -181,9 +220,12 @@ int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) {
 	if (!peerConnection)
 		return -1;
 
-	peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
-		cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
-	});
+	if (cb)
+		peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
+			cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
+		});
+	else
+		peerConnection->onGatheringStateChange(nullptr);
 	return 0;
 }
 
@@ -225,7 +267,22 @@ int rtcSetOpenCallback(int dc, openCallbackFunc cb) {
 	if (!dataChannel)
 		return -1;
 
-	dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
+	if (cb)
+		dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
+	else
+		dataChannel->onOpen(nullptr);
+	return 0;
+}
+
+int rtcSetClosedCallback(int dc, closedCallbackFunc cb) {
+	auto dataChannel = getDataChannel(dc);
+	if (!dataChannel)
+		return -1;
+
+	if (cb)
+		dataChannel->onClosed([dc, cb]() { cb(getUserPointer(dc)); });
+	else
+		dataChannel->onClosed(nullptr);
 	return 0;
 }
 
@@ -234,7 +291,11 @@ int rtcSetErrorCallback(int dc, errorCallbackFunc cb) {
 	if (!dataChannel)
 		return -1;
 
-	dataChannel->onError([dc, cb](const string &error) { cb(error.c_str(), getUserPointer(dc)); });
+	if (cb)
+		dataChannel->onError(
+		    [dc, cb](const string &error) { cb(error.c_str(), getUserPointer(dc)); });
+	else
+		dataChannel->onError(nullptr);
 	return 0;
 }
 
@@ -243,11 +304,15 @@ int rtcSetMessageCallback(int dc, messageCallbackFunc cb) {
 	if (!dataChannel)
 		return -1;
 
-	dataChannel->onMessage(
-	    [dc, cb](const binary &b) {
-		    cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(dc));
-	    },
-	    [dc, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(dc)); });
+	if (cb)
+		dataChannel->onMessage(
+		    [dc, cb](const binary &b) {
+			    cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(dc));
+		    },
+		    [dc, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(dc)); });
+	else
+		dataChannel->onMessage(nullptr);
+
 	return 0;
 }
 
@@ -289,7 +354,10 @@ int rtcSetBufferedAmountLowCallback(int dc, bufferedAmountLowCallbackFunc cb) {
 	if (!dataChannel)
 		return -1;
 
-	dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
+	if (cb)
+		dataChannel->onBufferedAmountLow([dc, cb]() { cb(getUserPointer(dc)); });
+	else
+		dataChannel->onBufferedAmountLow(nullptr);
 	return 0;
 }
 
@@ -306,7 +374,10 @@ int rtcSetAvailableCallback(int dc, availableCallbackFunc cb) {
 	if (!dataChannel)
 		return -1;
 
-	dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
+	if (cb)
+		dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
+	else
+		dataChannel->onOpen(nullptr);
 	return 0;
 }
 
@@ -333,11 +404,11 @@ int rtcReceiveMessage(int dc, char *buffer, int *size) {
 		               },
 		               [&](const string &s) {
 			               int len = std::min(*size - 1, int(s.size()));
-			               *size = -1;
 			               if (len >= 0) {
 				               std::copy(s.data(), s.data() + len, buffer);
 				               buffer[len] = '\0';
 			               }
+			               *size = -(len + 1);
 			               return len + 1;
 		               }},
 		    *message);

+ 178 - 0
test/capi.cpp

@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2020 Paul-Louis Ageneau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <rtc/rtc.h>
+
+#include <cstdbool>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <unistd.h> // for sleep
+
+using namespace std;
+
+typedef struct {
+	rtcState state;
+	rtcGatheringState gatheringState;
+	int pc;
+	int dc;
+	bool connected;
+} Peer;
+
+Peer *peer1 = NULL;
+Peer *peer2 = NULL;
+
+static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	printf("Description %d:\n%s\n", peer == peer1 ? 1 : 2, sdp);
+	Peer *other = peer == peer1 ? peer2 : peer1;
+	rtcSetRemoteDescription(other->pc, sdp, type);
+}
+
+static void candidateCallback(const char *cand, const char *mid, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	printf("Candidate %d: %s\n", peer == peer1 ? 1 : 2, cand);
+	Peer *other = peer == peer1 ? peer2 : peer1;
+	rtcAddRemoteCandidate(other->pc, cand, mid);
+}
+
+static void stateChangeCallback(rtcState state, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	peer->state = state;
+	printf("State %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
+}
+
+static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	peer->gatheringState = state;
+	printf("Gathering state %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
+}
+
+static void openCallback(void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	peer->connected = true;
+	printf("DataChannel %d: Open\n", peer == peer1 ? 1 : 2);
+
+	const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
+	rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
+}
+
+static void closedCallback(void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	peer->connected = false;
+}
+
+static void messageCallback(const char *message, int size, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	if (size < 0) { // negative size indicates a null-terminated string
+		printf("Message %d: %s\n", peer == peer1 ? 1 : 2, message);
+	} else {
+		printf("Message %d: [binary of size %d]\n", peer == peer1 ? 1 : 2, size);
+	}
+}
+
+static void dataChannelCallback(int dc, void *ptr) {
+	Peer *peer = (Peer *)ptr;
+	peer->dc = dc;
+	peer->connected = true;
+	rtcSetClosedCallback(dc, closedCallback);
+	rtcSetMessageCallback(dc, messageCallback);
+
+	char buffer[256];
+	if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
+		printf("DataChannel %d: Received with label \"%s\"\n", peer == peer1 ? 1 : 2, buffer);
+
+	const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
+	rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
+}
+
+static Peer *createPeer(const rtcConfiguration *config) {
+	Peer *peer = (Peer *)malloc(sizeof(Peer));
+	if (!peer)
+		return nullptr;
+	memset(peer, 0, sizeof(Peer));
+
+	// Create peer connection
+	peer->pc = rtcCreatePeerConnection(config);
+	rtcSetUserPointer(peer->pc, peer);
+	rtcSetDataChannelCallback(peer->pc, dataChannelCallback);
+	rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
+	rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
+	rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
+	rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
+
+	return peer;
+}
+
+static void deletePeer(Peer *peer) {
+	if (peer) {
+		if (peer->dc)
+			rtcDeleteDataChannel(peer->dc);
+		if (peer->pc)
+			rtcDeletePeerConnection(peer->pc);
+	}
+}
+
+int test_capi_main() {
+	rtcInitLogger(RTC_LOG_DEBUG);
+
+	rtcConfiguration config;
+	memset(&config, 0, sizeof(config));
+	// const char *iceServers[1] = {"stun:stun.l.google.com:19302"};
+	// config.iceServers = iceServers;
+	// config.iceServersCount = 1;
+
+	// Create peer 1
+	peer1 = createPeer(&config);
+	if (!peer1)
+		goto error;
+
+	// Create peer 2
+	peer2 = createPeer(&config);
+	if (!peer2)
+		goto error;
+
+	// Peer 1: Create data channel
+	peer1->dc = rtcCreateDataChannel(peer1->pc, "test");
+	rtcSetOpenCallback(peer1->dc, openCallback);
+	rtcSetClosedCallback(peer1->dc, closedCallback);
+	rtcSetMessageCallback(peer1->dc, messageCallback);
+
+	sleep(3);
+
+	if (peer1->connected && peer2->connected) {
+		deletePeer(peer1);
+		deletePeer(peer2);
+		sleep(1);
+		printf("Success\n");
+		return 0;
+	}
+
+error:
+	deletePeer(peer1);
+	deletePeer(peer2);
+	return -1;
+}
+
+#include <stdexcept>
+
+void test_capi() {
+	if (test_capi_main())
+		throw std::runtime_error("C API test failed");
+}

+ 5 - 6
test/connectivity.cpp

@@ -33,10 +33,9 @@ void test_connectivity() {
 
 	Configuration config;
 	// config.iceServers.emplace_back("stun:stun.l.google.com:19302");
-	// config.iceServers.emplace_back("turn:USER@PASSWORD:turn.example.net:3478?transport=udp"); //
-	// libnice only config.enableIceTcp = true; // libnice only
 
 	auto pc1 = std::make_shared<PeerConnection>(config);
+
 	auto pc2 = std::make_shared<PeerConnection>(config);
 
 	pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) {
@@ -83,11 +82,11 @@ void test_connectivity() {
 
 	shared_ptr<DataChannel> dc2;
 	pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
-		cout << "Got a DataChannel with label: " << dc->label() << endl;
+		cout << "DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
 		dc2 = dc;
 		dc2->onMessage([](const variant<binary, string> &message) {
 			if (holds_alternative<string>(message)) {
-				cout << "Received 2: " << get<string>(message) << endl;
+				cout << "Message 2: " << get<string>(message) << endl;
 			}
 		});
 		dc2->send("Hello from 2");
@@ -98,12 +97,12 @@ void test_connectivity() {
 		auto dc1 = wdc1.lock();
 		if (!dc1)
 			return;
-		cout << "DataChannel open: " << dc1->label() << endl;
+		cout << "DataChannel 1: Open" << endl;
 		dc1->send("Hello from 1");
 	});
 	dc1->onMessage([](const variant<binary, string> &message) {
 		if (holds_alternative<string>(message)) {
-			cout << "Received 1: " << get<string>(message) << endl;
+			cout << "Message 1: " << get<string>(message) << endl;
 		}
 	});
 

+ 12 - 2
test/main.cpp

@@ -21,14 +21,24 @@
 using namespace std;
 
 void test_connectivity();
+void test_capi();
 
 int main(int argc, char **argv) {
 	try {
+		std::cout << "*** Running connectivity test..." << std::endl;
 		test_connectivity();
+		std::cout << "*** Finished connectivity test" << std::endl;
 	} catch (const exception &e) {
-		std::cerr << "Connectivity check failed: " << e.what() << endl;
+		std::cerr << "Connectivity test failed: " << e.what() << endl;
+		return -1;
+	}
+	try {
+		std::cout << "*** Running C API test..." << std::endl;
+		test_capi();
+		std::cout << "*** Finished C API test" << std::endl;
+	} catch (const exception &e) {
+		std::cerr << "C API test failed: " << e.what() << endl;
 		return -1;
 	}
-
 	return 0;
 }