mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
becdaaa25b | |||
b6f2176be8 | |||
f7f83aa519 | |||
64e8957c54 | |||
f3b3208367 | |||
ed28460e80 | |||
7b5b12617d | |||
be04d8037e | |||
56198372fd | |||
29ffb34fe8 | |||
834ea9b041 | |||
9441f78494 | |||
3367eba4fe | |||
6507542a80 | |||
fea3297a57 | |||
f322ab00ec | |||
b6374b9d07 | |||
70fd54804d | |||
ff268aee60 | |||
91a5c608d7 | |||
682be73eab | |||
fd4a6fef7f | |||
05a06f47b0 | |||
8e3de8a07a | |||
dc065add0b | |||
e64d4049a6 | |||
cb3bc85474 | |||
7af3da7872 | |||
3c77d717d2 | |||
6f399945fe | |||
c8b14b1262 | |||
35d4455c4f | |||
7d21b4b42b |
21
.github/workflows/build.yml
vendored
Normal file
21
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
name: Build and test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
|
||||
- name: submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: cmake
|
||||
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
||||
- name: make
|
||||
run: (cd build; make)
|
||||
- name: test
|
||||
run: ./build/tests
|
||||
|
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required (VERSION 3.7)
|
||||
project (libdatachannel
|
||||
DESCRIPTION "WebRTC DataChannels Library"
|
||||
VERSION 0.4.4
|
||||
VERSION 0.4.7
|
||||
LANGUAGES CXX)
|
||||
|
||||
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
||||
@ -38,6 +38,8 @@ 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
|
||||
@ -64,16 +66,7 @@ target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includ
|
||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
||||
target_link_libraries(datachannel
|
||||
Threads::Threads
|
||||
Usrsctp::UsrsctpStatic
|
||||
)
|
||||
if(WIN32)
|
||||
target_link_libraries(datachannel
|
||||
"wsock32" #winsock2
|
||||
"ws2_32" #winsock2
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(datachannel Threads::Threads Usrsctp::UsrsctpStatic)
|
||||
|
||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
||||
set_target_properties(datachannel-static PROPERTIES
|
||||
@ -84,15 +77,11 @@ target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
||||
target_link_libraries(datachannel-static
|
||||
Threads::Threads
|
||||
Usrsctp::UsrsctpStatic
|
||||
)
|
||||
target_link_libraries(datachannel-static Threads::Threads Usrsctp::UsrsctpStatic)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(datachannel-static
|
||||
"wsock32" #winsock2
|
||||
"ws2_32" #winsock2
|
||||
)
|
||||
target_link_libraries(datachannel "wsock32" "ws2_32") # winsock2
|
||||
target_link_libraries(datachannel-static "wsock32" "ws2_32") # winsock2
|
||||
endif()
|
||||
|
||||
if (USE_GNUTLS)
|
||||
@ -156,6 +145,6 @@ add_executable(datachannel-answerer ${TESTS_ANSWERER_SOURCES})
|
||||
set_target_properties(datachannel-answerer PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
CXX_STANDARD 17)
|
||||
set_target_properties(datachannel-answerer PROPERTIES OUTPUT_NAME datachannel)
|
||||
set_target_properties(datachannel-answerer PROPERTIES OUTPUT_NAME answerer)
|
||||
target_link_libraries(datachannel-answerer datachannel)
|
||||
|
||||
|
7
Makefile
7
Makefile
@ -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
|
||||
|
@ -79,11 +79,11 @@ MY_ON_RECV_CANDIDATE_FROM_REMOTE([pc](string candidate, string mid) {
|
||||
### Observe the PeerConnection state
|
||||
|
||||
```cpp
|
||||
pc->onStateChanged([](PeerConnection::State state) {
|
||||
pc->onStateChange([](PeerConnection::State state) {
|
||||
cout << "State: " << state << endl;
|
||||
});
|
||||
|
||||
pc->onGatheringStateChanged([](PeerConnection::GatheringState state) {
|
||||
pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "Gathering state: " << state << endl;
|
||||
});
|
||||
|
||||
@ -114,5 +114,7 @@ pc->onDataChannel([&dc](shared_ptr<rtc::DataChannel> incoming) {
|
||||
|
||||
```
|
||||
|
||||
See [test/main.cpp](https://github.com/paullouisageneau/libdatachannel/blob/master/test/main.cpp) for a complete local connection example.
|
||||
See [test/connectivity.cpp](https://github.com/paullouisageneau/libdatachannel/blob/master/test/connectivity.cpp) for a complete local connection example.
|
||||
|
||||
See [test/cpai.cpp](https://github.com/paullouisageneau/libdatachannel/blob/master/test/capi.cpp) for a C API example.
|
||||
|
||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: 6ac73257b6...4154996a01
@ -31,12 +31,10 @@ class Channel {
|
||||
public:
|
||||
virtual void close() = 0;
|
||||
virtual bool send(const std::variant<binary, string> &data) = 0; // returns false if buffered
|
||||
virtual std::optional<std::variant<binary, string>> receive() = 0; // only if onMessage unset
|
||||
|
||||
virtual bool isOpen() const = 0;
|
||||
virtual bool isClosed() const = 0;
|
||||
|
||||
virtual size_t availableAmount() const; // total size available to receive
|
||||
virtual size_t maxMessageSize() const; // max message size in a call to send
|
||||
virtual size_t bufferedAmount() const; // total size buffered to send
|
||||
|
||||
void onOpen(std::function<void()> callback);
|
||||
@ -47,11 +45,14 @@ public:
|
||||
void onMessage(std::function<void(const binary &data)> binaryCallback,
|
||||
std::function<void(const string &data)> stringCallback);
|
||||
|
||||
void onAvailable(std::function<void()> callback);
|
||||
void onBufferedAmountLow(std::function<void()> callback);
|
||||
|
||||
void setBufferedAmountLowThreshold(size_t amount);
|
||||
|
||||
// Extended API
|
||||
virtual std::optional<std::variant<binary, string>> receive() = 0; // only if onMessage unset
|
||||
virtual size_t availableAmount() const; // total size available to receive
|
||||
void onAvailable(std::function<void()> callback);
|
||||
|
||||
protected:
|
||||
virtual void triggerOpen();
|
||||
virtual void triggerClosed();
|
||||
|
@ -44,27 +44,25 @@ public:
|
||||
unsigned int stream);
|
||||
~DataChannel();
|
||||
|
||||
void close(void) override;
|
||||
|
||||
bool send(const std::variant<binary, string> &data) override;
|
||||
bool send(const byte *data, size_t size);
|
||||
|
||||
template <typename Buffer> bool sendBuffer(const Buffer &buf);
|
||||
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
|
||||
|
||||
std::optional<std::variant<binary, string>> receive() override;
|
||||
|
||||
bool isOpen(void) const override;
|
||||
bool isClosed(void) const override;
|
||||
size_t availableAmount() const override;
|
||||
|
||||
size_t maxMessageSize() const; // maximum message size in a call to send or sendBuffer
|
||||
|
||||
unsigned int stream() const;
|
||||
string label() const;
|
||||
string protocol() const;
|
||||
Reliability reliability() const;
|
||||
|
||||
void close(void) override;
|
||||
bool send(const std::variant<binary, string> &data) override;
|
||||
bool send(const byte *data, size_t size);
|
||||
template <typename Buffer> bool sendBuffer(const Buffer &buf);
|
||||
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
|
||||
|
||||
bool isOpen(void) const override;
|
||||
bool isClosed(void) const override;
|
||||
size_t maxMessageSize() const override;
|
||||
|
||||
// Extended API
|
||||
size_t availableAmount() const override;
|
||||
std::optional<std::variant<binary, string>> receive() override;
|
||||
|
||||
private:
|
||||
void remoteClose();
|
||||
void open(std::shared_ptr<SctpTransport> sctpTransport);
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
enum class Role { ActPass = 0, Passive = 1, Active = 2 };
|
||||
|
||||
Description(const string &sdp, const string &typeString = "");
|
||||
Description(const string &sdp, Type type);
|
||||
Description(const string &sdp, Type type, Role role);
|
||||
|
||||
Type type() const;
|
||||
@ -47,6 +48,7 @@ public:
|
||||
std::optional<size_t> maxMessageSize() const;
|
||||
bool trickleEnabled() const;
|
||||
|
||||
void hintType(Type type);
|
||||
void setFingerprint(string fingerprint);
|
||||
void setSctpPort(uint16_t port);
|
||||
void setMaxMessageSize(size_t size);
|
||||
|
@ -20,11 +20,14 @@
|
||||
#define RTC_INCLUDE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0602
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@ -33,9 +36,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "plog/Appenders/ColorConsoleAppender.h"
|
||||
#include "plog/Log.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
using std::byte;
|
||||
@ -50,8 +50,6 @@ using std::uint32_t;
|
||||
using std::uint64_t;
|
||||
using std::uint8_t;
|
||||
|
||||
// Constants
|
||||
|
||||
const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length
|
||||
const size_t MAX_NUMERICSERV_LEN = 6; // Max port string representation length
|
||||
|
||||
@ -59,29 +57,6 @@ const uint16_t DEFAULT_SCTP_PORT = 5000; // SCTP port to use by default
|
||||
const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536; // Remote max message size if not specified in SDP
|
||||
const size_t LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Local max message size
|
||||
|
||||
// Log
|
||||
|
||||
enum class LogLevel { // Don't change, it must match plog severity
|
||||
None = 0,
|
||||
Fatal = 1,
|
||||
Error = 2,
|
||||
Warning = 3,
|
||||
Info = 4,
|
||||
Debug = 5,
|
||||
Verbose = 6
|
||||
};
|
||||
|
||||
inline void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr) {
|
||||
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
|
||||
if (!appender)
|
||||
appender = &consoleAppender;
|
||||
plog::init(severity, appender);
|
||||
PLOG_DEBUG << "Logger initialized";
|
||||
}
|
||||
|
||||
inline void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
|
||||
|
||||
// Utils
|
||||
|
||||
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
|
||||
@ -89,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) {
|
||||
|
55
include/rtc/log.hpp
Normal file
55
include/rtc/log.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2019 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
|
||||
*/
|
||||
|
||||
#ifndef RTC_LOG_H
|
||||
#define RTC_LOG_H
|
||||
|
||||
#include "plog/Appenders/ColorConsoleAppender.h"
|
||||
#include "plog/Log.h"
|
||||
#include "plog/Logger.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
enum class LogLevel { // Don't change, it must match plog severity
|
||||
None = 0,
|
||||
Fatal = 1,
|
||||
Error = 2,
|
||||
Warning = 3,
|
||||
Info = 4,
|
||||
Debug = 5,
|
||||
Verbose = 6
|
||||
};
|
||||
|
||||
inline void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr) {
|
||||
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
|
||||
static plog::Logger<0> *logger = nullptr;
|
||||
if (!logger) {
|
||||
logger = &plog::init(severity, appender ? appender : &consoleAppender);
|
||||
PLOG_DEBUG << "Logger initialized";
|
||||
} else {
|
||||
logger->setMaxSeverity(severity);
|
||||
if (appender)
|
||||
logger->addAppender(appender);
|
||||
}
|
||||
}
|
||||
|
||||
inline void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -32,6 +32,7 @@
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
@ -95,6 +96,11 @@ private:
|
||||
bool checkFingerprint(const std::string &fingerprint) const;
|
||||
void forwardMessage(message_ptr message);
|
||||
void forwardBufferedAmount(uint16_t stream, size_t amount);
|
||||
|
||||
std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, const string &label,
|
||||
const string &protocol,
|
||||
const Reliability &reliability);
|
||||
std::shared_ptr<DataChannel> findDataChannel(uint16_t stream);
|
||||
void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
|
||||
void openDataChannels();
|
||||
void closeDataChannels();
|
||||
@ -118,6 +124,7 @@ private:
|
||||
std::recursive_mutex mInitMutex;
|
||||
|
||||
std::unordered_map<unsigned int, std::weak_ptr<DataChannel>> mDataChannels;
|
||||
std::shared_mutex mDataChannelsMutex;
|
||||
|
||||
std::atomic<State> mState;
|
||||
std::atomic<GatheringState> mGatheringState;
|
||||
|
@ -33,13 +33,13 @@ typedef enum {
|
||||
RTC_FAILED = 4,
|
||||
RTC_CLOSED = 5,
|
||||
RTC_DESTROYING = 6 // internal
|
||||
} rtc_state_t;
|
||||
} rtcState;
|
||||
|
||||
typedef enum {
|
||||
RTC_GATHERING_NEW = 0,
|
||||
RTC_GATHERING_INPROGRESS = 1,
|
||||
RTC_GATHERING_COMPLETE = 2
|
||||
} rtc_gathering_state_t;
|
||||
} rtcGatheringState;
|
||||
|
||||
// Don't change, it must match plog severity
|
||||
typedef enum {
|
||||
@ -50,32 +50,67 @@ typedef enum {
|
||||
RTC_LOG_INFO = 4,
|
||||
RTC_LOG_DEBUG = 5,
|
||||
RTC_LOG_VERBOSE = 6
|
||||
} rtc_log_level_t;
|
||||
} rtcLogLevel;
|
||||
|
||||
void rtcInitLogger(rtc_log_level_t level);
|
||||
typedef struct {
|
||||
const char **iceServers;
|
||||
int iceServersCount;
|
||||
} rtcConfiguration;
|
||||
|
||||
int rtcCreatePeerConnection(const char **iceServers, int iceServersCount);
|
||||
void rtcDeletePeerConnection(int pc);
|
||||
int rtcCreateDataChannel(int pc, const char *label);
|
||||
void rtcDeleteDataChannel(int dc);
|
||||
void rtcSetDataChannelCallback(int pc, void (*dataChannelCallback)(int, void *));
|
||||
void rtcSetLocalDescriptionCallback(int pc, void (*descriptionCallback)(const char *, const char *,
|
||||
void *));
|
||||
void rtcSetLocalCandidateCallback(int pc,
|
||||
void (*candidateCallback)(const char *, const char *, void *));
|
||||
void rtcSetStateChangeCallback(int pc, void (*stateCallback)(rtc_state_t state, void *));
|
||||
void rtcSetGatheringStateChangeCallback(int pc,
|
||||
void (*gatheringStateCallback)(rtc_gathering_state_t state,
|
||||
void *));
|
||||
void rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
|
||||
void rtcAddRemoteCandidate(int pc, const char *candidate, const char *mid);
|
||||
int rtcGetDataChannelLabel(int dc, char *data, int size);
|
||||
void rtcSetOpenCallback(int dc, void (*openCallback)(void *));
|
||||
void rtcSetErrorCallback(int dc, void (*errorCallback)(const char *, void *));
|
||||
void rtcSetMessageCallback(int dc, void (*messageCallback)(const char *, int, void *));
|
||||
int rtcSendMessage(int dc, const char *data, int size);
|
||||
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);
|
||||
typedef void (*availableCallbackFunc)(void *ptr);
|
||||
|
||||
// Log
|
||||
void rtcInitLogger(rtcLogLevel level);
|
||||
|
||||
// User pointer
|
||||
void rtcSetUserPointer(int i, void *ptr);
|
||||
|
||||
// PeerConnection
|
||||
int rtcCreatePeerConnection(const rtcConfiguration *config);
|
||||
int rtcDeletePeerConnection(int pc);
|
||||
|
||||
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb);
|
||||
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb);
|
||||
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb);
|
||||
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb);
|
||||
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb);
|
||||
|
||||
int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
|
||||
int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
|
||||
|
||||
int rtcGetLocalAddress(int pc, char *buffer, int size);
|
||||
int rtcGetRemoteAddress(int pc, char *buffer, int size);
|
||||
|
||||
// DataChannel
|
||||
int rtcCreateDataChannel(int pc, const char *label);
|
||||
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);
|
||||
|
||||
int rtcGetBufferedAmount(int dc); // total size buffered to send
|
||||
int rtcSetBufferedAmountLowThreshold(int dc, int amount);
|
||||
int rtcSetBufferedAmountLowCallback(int dc, bufferedAmountLowCallbackFunc cb);
|
||||
|
||||
// DataChannel extended API
|
||||
int rtcGetAvailableAmount(int dc); // total size available to receive
|
||||
int rtcSetAvailableCallback(int dc, availableCallbackFunc cb);
|
||||
int rtcReceiveMessage(int dc, char *buffer, int *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
// C++ API
|
||||
#include "datachannel.hpp"
|
||||
#include "log.hpp"
|
||||
#include "peerconnection.hpp"
|
||||
|
||||
// C API
|
||||
|
@ -25,7 +25,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#elif __linux__
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
@ -18,10 +18,14 @@
|
||||
|
||||
#include "channel.hpp"
|
||||
|
||||
namespace {}
|
||||
|
||||
namespace rtc {
|
||||
|
||||
size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
|
||||
|
||||
size_t Channel::bufferedAmount() const { return mBufferedAmount; }
|
||||
|
||||
size_t Channel::availableAmount() const { return 0; }
|
||||
|
||||
void Channel::onOpen(std::function<void()> callback) {
|
||||
mOpenCallback = callback;
|
||||
}
|
||||
@ -49,20 +53,16 @@ void Channel::onMessage(std::function<void(const binary &data)> binaryCallback,
|
||||
});
|
||||
}
|
||||
|
||||
void Channel::onAvailable(std::function<void()> callback) {
|
||||
mAvailableCallback = callback;
|
||||
}
|
||||
|
||||
void Channel::onBufferedAmountLow(std::function<void()> callback) {
|
||||
mBufferedAmountLowCallback = callback;
|
||||
}
|
||||
|
||||
size_t Channel::availableAmount() const { return 0; }
|
||||
|
||||
size_t Channel::bufferedAmount() const { return mBufferedAmount; }
|
||||
|
||||
void Channel::setBufferedAmountLowThreshold(size_t amount) { mBufferedAmountLowThreshold = amount; }
|
||||
|
||||
void Channel::onAvailable(std::function<void()> callback) {
|
||||
mAvailableCallback = callback;
|
||||
}
|
||||
|
||||
void Channel::triggerOpen() { mOpenCallback(); }
|
||||
|
||||
void Channel::triggerClosed() { mClosedCallback(); }
|
||||
|
@ -21,6 +21,12 @@
|
||||
#include "peerconnection.hpp"
|
||||
#include "sctptransport.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
namespace rtc {
|
||||
|
||||
using std::shared_ptr;
|
||||
@ -77,6 +83,14 @@ DataChannel::~DataChannel() {
|
||||
close();
|
||||
}
|
||||
|
||||
unsigned int DataChannel::stream() const { return mStream; }
|
||||
|
||||
string DataChannel::label() const { return mLabel; }
|
||||
|
||||
string DataChannel::protocol() const { return mProtocol; }
|
||||
|
||||
Reliability DataChannel::reliability() const { return *mReliability; }
|
||||
|
||||
void DataChannel::close() {
|
||||
if (mIsOpen.exchange(false) && mSctpTransport)
|
||||
mSctpTransport->reset(mStream);
|
||||
@ -131,8 +145,6 @@ bool DataChannel::isOpen(void) const { return mIsOpen; }
|
||||
|
||||
bool DataChannel::isClosed(void) const { return mIsClosed; }
|
||||
|
||||
size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
|
||||
|
||||
size_t DataChannel::maxMessageSize() const {
|
||||
size_t max = DEFAULT_MAX_MESSAGE_SIZE;
|
||||
if (auto description = mPeerConnection->remoteDescription())
|
||||
@ -142,13 +154,7 @@ size_t DataChannel::maxMessageSize() const {
|
||||
return std::min(max, LOCAL_MAX_MESSAGE_SIZE);
|
||||
}
|
||||
|
||||
unsigned int DataChannel::stream() const { return mStream; }
|
||||
|
||||
string DataChannel::label() const { return mLabel; }
|
||||
|
||||
string DataChannel::protocol() const { return mProtocol; }
|
||||
|
||||
Reliability DataChannel::reliability() const { return *mReliability; }
|
||||
size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
|
||||
|
||||
void DataChannel::open(shared_ptr<SctpTransport> sctpTransport) {
|
||||
mSctpTransport = sctpTransport;
|
||||
|
@ -45,12 +45,13 @@ inline void trim_end(string &str) {
|
||||
namespace rtc {
|
||||
|
||||
Description::Description(const string &sdp, const string &typeString)
|
||||
: Description(sdp, stringToType(typeString), Description::Role::ActPass) {}
|
||||
: Description(sdp, stringToType(typeString)) {}
|
||||
|
||||
Description::Description(const string &sdp, Type type) : Description(sdp, type, Role::ActPass) {}
|
||||
|
||||
Description::Description(const string &sdp, Type type, Role role)
|
||||
: mType(type), mRole(role), mMid("0"), mIceUfrag("0"), mIcePwd("0"), mTrickle(true) {
|
||||
if (mType == Type::Answer && mRole == Role::ActPass)
|
||||
mRole = Role::Passive; // ActPass is illegal for an answer, so default to passive
|
||||
: mType(Type::Unspec), mRole(role), mMid("0"), mIceUfrag(""), mIcePwd(""), mTrickle(true) {
|
||||
hintType(type);
|
||||
|
||||
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::default_random_engine generator(seed);
|
||||
@ -109,6 +110,14 @@ std::optional<size_t> Description::maxMessageSize() const { return mMaxMessageSi
|
||||
|
||||
bool Description::trickleEnabled() const { return mTrickle; }
|
||||
|
||||
void Description::hintType(Type type) {
|
||||
if (mType == Type::Unspec) {
|
||||
mType = type;
|
||||
if (mType == Type::Answer && mRole == Role::ActPass)
|
||||
mRole = Role::Passive; // ActPass is illegal for an answer, so default to passive
|
||||
}
|
||||
}
|
||||
|
||||
void Description::setFingerprint(string fingerprint) {
|
||||
mFingerprint.emplace(std::move(fingerprint));
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
||||
// RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
|
||||
// Therefore, the DTLS layer MUST NOT use any compression algorithm.
|
||||
// See https://tools.ietf.org/html/rfc8261#section-5
|
||||
const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL";
|
||||
const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
|
||||
const char *err_pos = NULL;
|
||||
check_gnutls(gnutls_priority_set_direct(mSession, priorities, &err_pos),
|
||||
"Unable to set TLS priorities");
|
||||
|
@ -19,9 +19,15 @@
|
||||
#include "icetransport.hpp"
|
||||
#include "configuration.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#elif __linux__
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
@ -29,10 +35,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using std::shared_ptr;
|
||||
@ -114,9 +116,6 @@ void IceTransport::setRemoteDescription(const Description &description) {
|
||||
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
||||
: Description::Role::Active;
|
||||
mMid = description.mid();
|
||||
// TODO
|
||||
// mTrickleTimeout = description.trickleEnabled() ? 30s : 0s;
|
||||
|
||||
if (juice_set_remote_description(mAgent.get(), string(description).c_str()) < 0)
|
||||
throw std::runtime_error("Failed to parse remote SDP");
|
||||
}
|
||||
@ -246,11 +245,8 @@ void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
|
||||
case JUICE_LOG_LEVEL_INFO:
|
||||
severity = plog::info;
|
||||
break;
|
||||
case JUICE_LOG_LEVEL_DEBUG:
|
||||
severity = plog::debug;
|
||||
break;
|
||||
default:
|
||||
severity = plog::verbose;
|
||||
severity = plog::verbose; // libjuice debug as verbose
|
||||
break;
|
||||
}
|
||||
PLOG(severity) << "juice: " << message;
|
||||
|
@ -25,6 +25,10 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
namespace rtc {
|
||||
|
||||
using namespace std::placeholders;
|
||||
@ -32,7 +36,13 @@ using namespace std::placeholders;
|
||||
using std::shared_ptr;
|
||||
using std::weak_ptr;
|
||||
|
||||
PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
|
||||
PeerConnection::PeerConnection() : PeerConnection(Configuration()) {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
|
||||
throw std::runtime_error("WSAStartup failed, error=" + std::to_string(WSAGetLastError()));
|
||||
#endif
|
||||
}
|
||||
|
||||
PeerConnection::PeerConnection(const Configuration &config)
|
||||
: mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New) {}
|
||||
@ -43,12 +53,15 @@ PeerConnection::~PeerConnection() {
|
||||
mSctpTransport.reset();
|
||||
mDtlsTransport.reset();
|
||||
mIceTransport.reset();
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PeerConnection::close() {
|
||||
// Close DataChannels
|
||||
closeDataChannels();
|
||||
mDataChannels.clear();
|
||||
|
||||
// Close Transports
|
||||
for (int i = 0; i < 2; ++i) { // Make sure a transport wasn't spawn behind our back
|
||||
@ -79,9 +92,10 @@ std::optional<Description> PeerConnection::remoteDescription() const {
|
||||
}
|
||||
|
||||
void PeerConnection::setRemoteDescription(Description description) {
|
||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||
|
||||
description.hintType(localDescription() ? Description::Type::Answer : Description::Type::Offer);
|
||||
auto remoteCandidates = description.extractCandidates();
|
||||
|
||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||
mRemoteDescription.emplace(std::move(description));
|
||||
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
@ -100,12 +114,16 @@ void PeerConnection::setRemoteDescription(Description description) {
|
||||
if (!sctpTransport && iceTransport->role() == Description::Role::Active) {
|
||||
// Since we assumed passive role during DataChannel creation, we need to shift the
|
||||
// stream numbers by one to shift them from odd to even.
|
||||
std::unique_lock lock(mDataChannelsMutex);
|
||||
decltype(mDataChannels) newDataChannels;
|
||||
iterateDataChannels([&](shared_ptr<DataChannel> channel) {
|
||||
auto it = mDataChannels.begin();
|
||||
while (it != mDataChannels.end()) {
|
||||
auto channel = it->second.lock();
|
||||
if (channel->stream() % 2 == 1)
|
||||
channel->mStream -= 1;
|
||||
newDataChannels.emplace(channel->stream(), channel);
|
||||
});
|
||||
++it;
|
||||
}
|
||||
std::swap(mDataChannels, newDataChannels);
|
||||
}
|
||||
}
|
||||
@ -157,19 +175,7 @@ shared_ptr<DataChannel> PeerConnection::createDataChannel(const string &label,
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
|
||||
|
||||
// The active side must use streams with even identifiers, whereas the passive side must use
|
||||
// streams with odd identifiers.
|
||||
// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
|
||||
unsigned int stream = (role == Description::Role::Active) ? 0 : 1;
|
||||
while (mDataChannels.find(stream) != mDataChannels.end()) {
|
||||
stream += 2;
|
||||
if (stream >= 65535)
|
||||
throw std::runtime_error("Too many DataChannels");
|
||||
}
|
||||
|
||||
auto channel =
|
||||
std::make_shared<DataChannel>(shared_from_this(), stream, label, protocol, reliability);
|
||||
mDataChannels.insert(std::make_pair(stream, channel));
|
||||
auto channel = emplaceDataChannel(role, label, protocol, reliability);
|
||||
|
||||
if (!iceTransport) {
|
||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||
@ -209,108 +215,126 @@ void PeerConnection::onGatheringStateChange(std::function<void(GatheringState st
|
||||
}
|
||||
|
||||
shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role) {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mIceTransport))
|
||||
return transport;
|
||||
try {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mIceTransport))
|
||||
return transport;
|
||||
|
||||
auto transport = std::make_shared<IceTransport>(
|
||||
mConfig, role, std::bind(&PeerConnection::processLocalCandidate, this, _1),
|
||||
[this](IceTransport::State state) {
|
||||
switch (state) {
|
||||
case IceTransport::State::Connecting:
|
||||
changeState(State::Connecting);
|
||||
break;
|
||||
case IceTransport::State::Failed:
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case IceTransport::State::Connected:
|
||||
initDtlsTransport();
|
||||
break;
|
||||
case IceTransport::State::Disconnected:
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
},
|
||||
[this](IceTransport::GatheringState state) {
|
||||
switch (state) {
|
||||
case IceTransport::GatheringState::InProgress:
|
||||
changeGatheringState(GatheringState::InProgress);
|
||||
break;
|
||||
case IceTransport::GatheringState::Complete:
|
||||
endLocalCandidates();
|
||||
changeGatheringState(GatheringState::Complete);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mIceTransport, transport);
|
||||
return transport;
|
||||
auto transport = std::make_shared<IceTransport>(
|
||||
mConfig, role, std::bind(&PeerConnection::processLocalCandidate, this, _1),
|
||||
[this](IceTransport::State state) {
|
||||
switch (state) {
|
||||
case IceTransport::State::Connecting:
|
||||
changeState(State::Connecting);
|
||||
break;
|
||||
case IceTransport::State::Failed:
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case IceTransport::State::Connected:
|
||||
initDtlsTransport();
|
||||
break;
|
||||
case IceTransport::State::Disconnected:
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
},
|
||||
[this](IceTransport::GatheringState state) {
|
||||
switch (state) {
|
||||
case IceTransport::GatheringState::InProgress:
|
||||
changeGatheringState(GatheringState::InProgress);
|
||||
break;
|
||||
case IceTransport::GatheringState::Complete:
|
||||
endLocalCandidates();
|
||||
changeGatheringState(GatheringState::Complete);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mIceTransport, transport);
|
||||
return transport;
|
||||
} catch (const std::exception &e) {
|
||||
PLOG_ERROR << e.what();
|
||||
changeState(State::Failed);
|
||||
throw std::runtime_error("ICE transport initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mDtlsTransport))
|
||||
return transport;
|
||||
try {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mDtlsTransport))
|
||||
return transport;
|
||||
|
||||
auto lower = std::atomic_load(&mIceTransport);
|
||||
auto transport = std::make_shared<DtlsTransport>(
|
||||
lower, mCertificate, std::bind(&PeerConnection::checkFingerprint, this, _1),
|
||||
[this](DtlsTransport::State state) {
|
||||
switch (state) {
|
||||
case DtlsTransport::State::Connected:
|
||||
initSctpTransport();
|
||||
break;
|
||||
case DtlsTransport::State::Failed:
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case DtlsTransport::State::Disconnected:
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mDtlsTransport, transport);
|
||||
return transport;
|
||||
auto lower = std::atomic_load(&mIceTransport);
|
||||
auto transport = std::make_shared<DtlsTransport>(
|
||||
lower, mCertificate, std::bind(&PeerConnection::checkFingerprint, this, _1),
|
||||
[this](DtlsTransport::State state) {
|
||||
switch (state) {
|
||||
case DtlsTransport::State::Connected:
|
||||
initSctpTransport();
|
||||
break;
|
||||
case DtlsTransport::State::Failed:
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case DtlsTransport::State::Disconnected:
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mDtlsTransport, transport);
|
||||
return transport;
|
||||
} catch (const std::exception &e) {
|
||||
PLOG_ERROR << e.what();
|
||||
changeState(State::Failed);
|
||||
throw std::runtime_error("DTLS transport initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mSctpTransport))
|
||||
return transport;
|
||||
try {
|
||||
std::lock_guard lock(mInitMutex);
|
||||
if (auto transport = std::atomic_load(&mSctpTransport))
|
||||
return transport;
|
||||
|
||||
uint16_t sctpPort = remoteDescription()->sctpPort().value_or(DEFAULT_SCTP_PORT);
|
||||
auto lower = std::atomic_load(&mDtlsTransport);
|
||||
auto transport = std::make_shared<SctpTransport>(
|
||||
lower, sctpPort, std::bind(&PeerConnection::forwardMessage, this, _1),
|
||||
std::bind(&PeerConnection::forwardBufferedAmount, this, _1, _2),
|
||||
[this](SctpTransport::State state) {
|
||||
switch (state) {
|
||||
case SctpTransport::State::Connected:
|
||||
changeState(State::Connected);
|
||||
openDataChannels();
|
||||
break;
|
||||
case SctpTransport::State::Failed:
|
||||
remoteCloseDataChannels();
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case SctpTransport::State::Disconnected:
|
||||
remoteCloseDataChannels();
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mSctpTransport, transport);
|
||||
return transport;
|
||||
uint16_t sctpPort = remoteDescription()->sctpPort().value_or(DEFAULT_SCTP_PORT);
|
||||
auto lower = std::atomic_load(&mDtlsTransport);
|
||||
auto transport = std::make_shared<SctpTransport>(
|
||||
lower, sctpPort, std::bind(&PeerConnection::forwardMessage, this, _1),
|
||||
std::bind(&PeerConnection::forwardBufferedAmount, this, _1, _2),
|
||||
[this](SctpTransport::State state) {
|
||||
switch (state) {
|
||||
case SctpTransport::State::Connected:
|
||||
changeState(State::Connected);
|
||||
openDataChannels();
|
||||
break;
|
||||
case SctpTransport::State::Failed:
|
||||
remoteCloseDataChannels();
|
||||
changeState(State::Failed);
|
||||
break;
|
||||
case SctpTransport::State::Disconnected:
|
||||
remoteCloseDataChannels();
|
||||
changeState(State::Disconnected);
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
});
|
||||
std::atomic_store(&mSctpTransport, transport);
|
||||
return transport;
|
||||
} catch (const std::exception &e) {
|
||||
PLOG_ERROR << e.what();
|
||||
changeState(State::Failed);
|
||||
throw std::runtime_error("SCTP transport initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnection::endLocalCandidates() {
|
||||
@ -334,14 +358,7 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
||||
return;
|
||||
}
|
||||
|
||||
shared_ptr<DataChannel> channel;
|
||||
if (auto it = mDataChannels.find(message->stream); it != mDataChannels.end()) {
|
||||
channel = it->second.lock();
|
||||
if (!channel || channel->isClosed()) {
|
||||
mDataChannels.erase(it);
|
||||
channel = nullptr;
|
||||
}
|
||||
}
|
||||
auto channel = findDataChannel(message->stream);
|
||||
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||
@ -369,21 +386,46 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
||||
}
|
||||
|
||||
void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
|
||||
if (auto channel = findDataChannel(stream))
|
||||
channel->triggerBufferedAmount(amount);
|
||||
}
|
||||
|
||||
shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role,
|
||||
const string &label,
|
||||
const string &protocol,
|
||||
const Reliability &reliability) {
|
||||
// The active side must use streams with even identifiers, whereas the passive side must use
|
||||
// streams with odd identifiers.
|
||||
// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
|
||||
std::unique_lock lock(mDataChannelsMutex);
|
||||
unsigned int stream = (role == Description::Role::Active) ? 0 : 1;
|
||||
while (mDataChannels.find(stream) != mDataChannels.end()) {
|
||||
stream += 2;
|
||||
if (stream >= 65535)
|
||||
throw std::runtime_error("Too many DataChannels");
|
||||
}
|
||||
auto channel =
|
||||
std::make_shared<DataChannel>(shared_from_this(), stream, label, protocol, reliability);
|
||||
mDataChannels.emplace(std::make_pair(stream, channel));
|
||||
return channel;
|
||||
}
|
||||
|
||||
shared_ptr<DataChannel> PeerConnection::findDataChannel(uint16_t stream) {
|
||||
std::shared_lock lock(mDataChannelsMutex);
|
||||
shared_ptr<DataChannel> channel;
|
||||
if (auto it = mDataChannels.find(stream); it != mDataChannels.end()) {
|
||||
channel = it->second.lock();
|
||||
if (!channel || channel->isClosed()) {
|
||||
mDataChannels.erase(it);
|
||||
channel = nullptr;
|
||||
channel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (channel)
|
||||
channel->triggerBufferedAmount(amount);
|
||||
return channel;
|
||||
}
|
||||
|
||||
void PeerConnection::iterateDataChannels(
|
||||
std::function<void(shared_ptr<DataChannel> channel)> func) {
|
||||
std::shared_lock lock(mDataChannelsMutex);
|
||||
auto it = mDataChannels.begin();
|
||||
while (it != mDataChannels.end()) {
|
||||
auto channel = it->second.lock();
|
||||
|
532
src/rtc.cpp
532
src/rtc.cpp
@ -22,191 +22,82 @@
|
||||
|
||||
#include <rtc.h>
|
||||
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <plog/Appenders/ColorConsoleAppender.h>
|
||||
#include <utility>
|
||||
|
||||
using namespace rtc;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
|
||||
#define CATCH(statement) \
|
||||
try { \
|
||||
statement; \
|
||||
} catch (const std::exception &e) { \
|
||||
PLOG_ERROR << e.what(); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
|
||||
std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
|
||||
std::unordered_map<int, void *> userPointerMap;
|
||||
std::mutex mutex;
|
||||
int lastId = 0;
|
||||
|
||||
void *getUserPointer(int id) {
|
||||
std::lock_guard lock(mutex);
|
||||
auto it = userPointerMap.find(id);
|
||||
return it != userPointerMap.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
shared_ptr<PeerConnection> getPeerConnection(int id) {
|
||||
std::lock_guard lock(mutex);
|
||||
auto it = peerConnectionMap.find(id);
|
||||
return it != peerConnectionMap.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void rtcInitLogger(rtc_log_level_t level) { InitLogger(static_cast<LogLevel>(level)); }
|
||||
shared_ptr<DataChannel> getDataChannel(int id) {
|
||||
std::lock_guard lock(mutex);
|
||||
auto it = dataChannelMap.find(id);
|
||||
return it != dataChannelMap.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
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 emplacePeerConnection(shared_ptr<PeerConnection> ptr) {
|
||||
std::lock_guard lock(mutex);
|
||||
int pc = ++lastId;
|
||||
peerConnectionMap.emplace(std::make_pair(pc, std::make_shared<PeerConnection>(config)));
|
||||
peerConnectionMap.emplace(std::make_pair(pc, ptr));
|
||||
return pc;
|
||||
}
|
||||
|
||||
void rtcDeletePeerConnection(int pc) { peerConnectionMap.erase(pc); }
|
||||
|
||||
int rtcCreateDataChannel(int pc, const char *label) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return 0;
|
||||
auto dataChannel = it->second->createDataChannel(string(label));
|
||||
int emplaceDataChannel(shared_ptr<DataChannel> ptr) {
|
||||
std::lock_guard lock(mutex);
|
||||
int dc = ++lastId;
|
||||
dataChannelMap.emplace(std::make_pair(dc, dataChannel));
|
||||
dataChannelMap.emplace(std::make_pair(dc, ptr));
|
||||
return dc;
|
||||
}
|
||||
|
||||
void rtcDeleteDataChannel(int dc) { dataChannelMap.erase(dc); }
|
||||
|
||||
void rtcSetDataChannelCallback(int pc, void (*dataChannelCallback)(int, void *)) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->onDataChannel([pc, dataChannelCallback](std::shared_ptr<DataChannel> dataChannel) {
|
||||
int dc = ++lastId;
|
||||
dataChannelMap.emplace(std::make_pair(dc, dataChannel));
|
||||
dataChannelCallback(dc, getUserPointer(pc));
|
||||
});
|
||||
bool erasePeerConnection(int pc) {
|
||||
std::lock_guard lock(mutex);
|
||||
if (peerConnectionMap.erase(pc) == 0)
|
||||
return false;
|
||||
userPointerMap.erase(pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rtcSetLocalDescriptionCallback(int pc, void (*descriptionCallback)(const char *, const char *,
|
||||
void *)) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->onLocalDescription([pc, descriptionCallback](const Description &description) {
|
||||
descriptionCallback(string(description).c_str(), description.typeString().c_str(),
|
||||
getUserPointer(pc));
|
||||
});
|
||||
bool eraseDataChannel(int dc) {
|
||||
std::lock_guard lock(mutex);
|
||||
if (dataChannelMap.erase(dc) == 0)
|
||||
return false;
|
||||
userPointerMap.erase(dc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rtcSetLocalCandidateCallback(int pc,
|
||||
void (*candidateCallback)(const char *, const char *, void *)) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
} // namespace
|
||||
|
||||
it->second->onLocalCandidate([pc, candidateCallback](const Candidate &candidate) {
|
||||
candidateCallback(candidate.candidate().c_str(), candidate.mid().c_str(),
|
||||
getUserPointer(pc));
|
||||
});
|
||||
}
|
||||
|
||||
void rtcSetStateChangeCallback(int pc, void (*stateCallback)(rtc_state_t state, void *)) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->onStateChange([pc, stateCallback](PeerConnection::State state) {
|
||||
stateCallback(static_cast<rtc_state_t>(state), getUserPointer(pc));
|
||||
});
|
||||
}
|
||||
|
||||
void rtcSetGatheringStateChangeCallback(int pc,
|
||||
void (*gatheringStateCallback)(rtc_gathering_state_t state,
|
||||
void *)) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->onGatheringStateChange(
|
||||
[pc, gatheringStateCallback](PeerConnection::GatheringState state) {
|
||||
gatheringStateCallback(static_cast<rtc_gathering_state_t>(state), getUserPointer(pc));
|
||||
});
|
||||
}
|
||||
|
||||
void rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->setRemoteDescription(Description(string(sdp), type ? string(type) : ""));
|
||||
}
|
||||
|
||||
void rtcAddRemoteCandidate(int pc, const char *candidate, const char *mid) {
|
||||
auto it = peerConnectionMap.find(pc);
|
||||
if (it == peerConnectionMap.end())
|
||||
return;
|
||||
|
||||
it->second->addRemoteCandidate(Candidate(string(candidate), mid ? string(mid) : ""));
|
||||
}
|
||||
|
||||
int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
|
||||
auto it = dataChannelMap.find(dc);
|
||||
if (it == dataChannelMap.end())
|
||||
return 0;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
string label = it->second->label();
|
||||
size = std::min(size_t(size - 1), label.size());
|
||||
std::copy(label.data(), label.data() + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
void rtcSetOpenCallback(int dc, void (*openCallback)(void *)) {
|
||||
auto it = dataChannelMap.find(dc);
|
||||
if (it == dataChannelMap.end())
|
||||
return;
|
||||
|
||||
it->second->onOpen([dc, openCallback]() { openCallback(getUserPointer(dc)); });
|
||||
}
|
||||
|
||||
void rtcSetErrorCallback(int dc, void (*errorCallback)(const char *, void *)) {
|
||||
auto it = dataChannelMap.find(dc);
|
||||
if (it == dataChannelMap.end())
|
||||
return;
|
||||
|
||||
it->second->onError([dc, errorCallback](const string &error) {
|
||||
errorCallback(error.c_str(), getUserPointer(dc));
|
||||
});
|
||||
}
|
||||
|
||||
void rtcSetMessageCallback(int dc, void (*messageCallback)(const char *, int, void *)) {
|
||||
auto it = dataChannelMap.find(dc);
|
||||
if (it == dataChannelMap.end())
|
||||
return;
|
||||
|
||||
it->second->onMessage(
|
||||
[dc, messageCallback](const binary &b) {
|
||||
messageCallback(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(dc));
|
||||
},
|
||||
[dc, messageCallback](const string &s) {
|
||||
messageCallback(s.c_str(), -1, getUserPointer(dc));
|
||||
});
|
||||
}
|
||||
|
||||
int rtcSendMessage(int dc, const char *data, int size) {
|
||||
auto it = dataChannelMap.find(dc);
|
||||
if (it == dataChannelMap.end())
|
||||
return 0;
|
||||
|
||||
if (size >= 0) {
|
||||
auto b = reinterpret_cast<const byte *>(data);
|
||||
it->second->send(b, size);
|
||||
return size;
|
||||
} else {
|
||||
string s(data);
|
||||
it->second->send(s);
|
||||
return s.size();
|
||||
}
|
||||
}
|
||||
void rtcInitLogger(rtcLogLevel level) { InitLogger(static_cast<LogLevel>(level)); }
|
||||
|
||||
void rtcSetUserPointer(int i, void *ptr) {
|
||||
if (ptr)
|
||||
@ -214,3 +105,340 @@ void rtcSetUserPointer(int i, void *ptr) {
|
||||
else
|
||||
userPointerMap.erase(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>(c));
|
||||
}
|
||||
|
||||
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);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
int dc = emplaceDataChannel(peerConnection->createDataChannel(string(label)));
|
||||
void *ptr = getUserPointer(pc);
|
||||
rtcSetUserPointer(dc, ptr);
|
||||
return dc;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
if (cb)
|
||||
peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
|
||||
cb(static_cast<rtcState>(state), getUserPointer(pc));
|
||||
});
|
||||
else
|
||||
peerConnection->onStateChange(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
if (cb)
|
||||
peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
|
||||
cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
|
||||
});
|
||||
else
|
||||
peerConnection->onGatheringStateChange(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
CATCH(peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
CATCH(peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""}))
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcGetLocalAddress(int pc, char *buffer, int size) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
if (auto addr = peerConnection->localAddress()) {
|
||||
size = std::min(size_t(size - 1), addr->size());
|
||||
std::copy(addr->data(), addr->data() + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rtcGetRemoteAddress(int pc, char *buffer, int size) {
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
if (!peerConnection)
|
||||
return -1;
|
||||
|
||||
if (auto addr = peerConnection->remoteAddress()) {
|
||||
size = std::min(size_t(size - 1), addr->size());
|
||||
std::copy(addr->data(), addr->data() + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
string label = dataChannel->label();
|
||||
size = std::min(size_t(size - 1), label.size());
|
||||
std::copy(label.data(), label.data() + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
int rtcSetOpenCallback(int dc, openCallbackFunc cb) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rtcSetErrorCallback(int dc, errorCallbackFunc cb) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (cb)
|
||||
dataChannel->onError(
|
||||
[dc, cb](const string &error) { cb(error.c_str(), getUserPointer(dc)); });
|
||||
else
|
||||
dataChannel->onError(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcSetMessageCallback(int dc, messageCallbackFunc cb) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rtcSendMessage(int dc, const char *data, int size) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (size >= 0) {
|
||||
auto b = reinterpret_cast<const byte *>(data);
|
||||
CATCH(dataChannel->send(b, size));
|
||||
return size;
|
||||
} else {
|
||||
string s(data);
|
||||
CATCH(dataChannel->send(s));
|
||||
return s.size();
|
||||
}
|
||||
}
|
||||
|
||||
int rtcGetBufferedAmount(int dc) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
CATCH(return int(dataChannel->bufferedAmount()));
|
||||
}
|
||||
|
||||
int rtcSetBufferedAmountLowThreshold(int dc, int amount) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
CATCH(dataChannel->setBufferedAmountLowThreshold(size_t(amount)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcSetBufferedAmountLowCallback(int dc, bufferedAmountLowCallbackFunc cb) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (cb)
|
||||
dataChannel->onBufferedAmountLow([dc, cb]() { cb(getUserPointer(dc)); });
|
||||
else
|
||||
dataChannel->onBufferedAmountLow(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcGetAvailableAmount(int dc) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
CATCH(return int(dataChannel->availableAmount()));
|
||||
}
|
||||
|
||||
int rtcSetAvailableCallback(int dc, availableCallbackFunc cb) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (cb)
|
||||
dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
|
||||
else
|
||||
dataChannel->onOpen(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtcReceiveMessage(int dc, char *buffer, int *size) {
|
||||
auto dataChannel = getDataChannel(dc);
|
||||
if (!dataChannel)
|
||||
return -1;
|
||||
|
||||
if (!size)
|
||||
return -1;
|
||||
|
||||
CATCH({
|
||||
auto message = dataChannel->receive();
|
||||
if (!message)
|
||||
return 0;
|
||||
|
||||
return std::visit( //
|
||||
overloaded{ //
|
||||
[&](const binary &b) {
|
||||
*size = std::min(*size, int(b.size()));
|
||||
auto data = reinterpret_cast<const char *>(b.data());
|
||||
std::copy(data, data + *size, buffer);
|
||||
return *size;
|
||||
},
|
||||
[&](const string &s) {
|
||||
int len = std::min(*size - 1, int(s.size()));
|
||||
if (len >= 0) {
|
||||
std::copy(s.data(), s.data() + len, buffer);
|
||||
buffer[len] = '\0';
|
||||
}
|
||||
*size = -(len + 1);
|
||||
return len + 1;
|
||||
}},
|
||||
*message);
|
||||
});
|
||||
}
|
||||
|
@ -23,8 +23,23 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_JUICE
|
||||
#ifndef __APPLE__
|
||||
// libjuice enables Linux path MTU discovery or sets the DF flag
|
||||
#define USE_PMTUD 1
|
||||
#else
|
||||
// Setting the DF flag is not available on Mac OS
|
||||
#define USE_PMTUD 0
|
||||
#endif
|
||||
#else
|
||||
#ifdef __linux__
|
||||
#include <arpa/inet.h>
|
||||
// Linux UDP does path MTU discovery by default (setting DF and returning EMSGSIZE)
|
||||
// It should be safe to enable discovery for SCTP.
|
||||
#define USE_PMTUD 1
|
||||
#else
|
||||
// Otherwise assume fragmentation
|
||||
#define USE_PMTUD 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
@ -119,12 +134,11 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
||||
std::to_string(errno));
|
||||
|
||||
struct sctp_paddrparams spp = {};
|
||||
#ifdef __linux__
|
||||
// Linux UDP does path MTU discovery by default (setting DF and returning EMSGSIZE).
|
||||
// It should be safe to enable discovery for SCTP.
|
||||
#if USE_PMTUD
|
||||
// Enabled SCTP path MTU discovery
|
||||
spp.spp_flags = SPP_PMTUD_ENABLE;
|
||||
#else
|
||||
// Otherwise, fall back to a safe MTU value.
|
||||
// Fall back to a safe MTU value.
|
||||
spp.spp_flags = SPP_PMTUD_DISABLE;
|
||||
spp.spp_pathmtu = 1200; // Max safe value recommended by RFC 8261
|
||||
// See https://tools.ietf.org/html/rfc8261#section-5
|
||||
@ -355,7 +369,7 @@ bool SctpTransport::trySendMessage(message_ptr message) {
|
||||
if (ret >= 0) {
|
||||
PLOG_VERBOSE << "SCTP sent size=" << message->size();
|
||||
return true;
|
||||
} else if (errno == EWOULDBLOCK && errno == EAGAIN) {
|
||||
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
PLOG_VERBOSE << "SCTP sending not possible";
|
||||
return false;
|
||||
} else {
|
||||
|
@ -29,14 +29,6 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#elif __linux__
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "usrsctp.h"
|
||||
|
||||
namespace rtc {
|
||||
|
188
test/capi.cpp
Normal file
188
test/capi.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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);
|
||||
|
||||
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("Connection failed");
|
||||
}
|
129
test/connectivity.cpp
Normal file
129
test/connectivity.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2019 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.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
using namespace rtc;
|
||||
using namespace std;
|
||||
|
||||
template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
|
||||
|
||||
void test_connectivity() {
|
||||
InitLogger(LogLevel::Debug);
|
||||
|
||||
Configuration config;
|
||||
// config.iceServers.emplace_back("stun:stun.l.google.com:19302");
|
||||
|
||||
auto pc1 = std::make_shared<PeerConnection>(config);
|
||||
|
||||
auto pc2 = std::make_shared<PeerConnection>(config);
|
||||
|
||||
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) {
|
||||
auto pc2 = wpc2.lock();
|
||||
if (!pc2)
|
||||
return;
|
||||
cout << "Description 1: " << sdp << endl;
|
||||
pc2->setRemoteDescription(sdp);
|
||||
});
|
||||
|
||||
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](const Candidate &candidate) {
|
||||
auto pc2 = wpc2.lock();
|
||||
if (!pc2)
|
||||
return;
|
||||
cout << "Candidate 1: " << candidate << endl;
|
||||
pc2->addRemoteCandidate(candidate);
|
||||
});
|
||||
|
||||
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
|
||||
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "Gathering state 1: " << state << endl;
|
||||
});
|
||||
|
||||
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](const Description &sdp) {
|
||||
auto pc1 = wpc1.lock();
|
||||
if (!pc1)
|
||||
return;
|
||||
cout << "Description 2: " << sdp << endl;
|
||||
pc1->setRemoteDescription(sdp);
|
||||
});
|
||||
|
||||
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](const Candidate &candidate) {
|
||||
auto pc1 = wpc1.lock();
|
||||
if (!pc1)
|
||||
return;
|
||||
cout << "Candidate 2: " << candidate << endl;
|
||||
pc1->addRemoteCandidate(candidate);
|
||||
});
|
||||
|
||||
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
|
||||
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "Gathering state 2: " << state << endl;
|
||||
});
|
||||
|
||||
shared_ptr<DataChannel> dc2;
|
||||
pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
|
||||
cout << "DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
|
||||
dc2 = dc;
|
||||
dc2->onMessage([](const variant<binary, string> &message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "Message 2: " << get<string>(message) << endl;
|
||||
}
|
||||
});
|
||||
dc2->send("Hello from 2");
|
||||
});
|
||||
|
||||
auto dc1 = pc1->createDataChannel("test");
|
||||
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
|
||||
auto dc1 = wdc1.lock();
|
||||
if (!dc1)
|
||||
return;
|
||||
cout << "DataChannel 1: Open" << endl;
|
||||
dc1->send("Hello from 1");
|
||||
});
|
||||
dc1->onMessage([](const variant<binary, string> &message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "Message 1: " << get<string>(message) << endl;
|
||||
}
|
||||
});
|
||||
|
||||
this_thread::sleep_for(3s);
|
||||
|
||||
if (auto addr = pc1->localAddress())
|
||||
cout << "Local address 1: " << *addr << endl;
|
||||
if (auto addr = pc1->remoteAddress())
|
||||
cout << "Remote address 1: " << *addr << endl;
|
||||
if (auto addr = pc2->localAddress())
|
||||
cout << "Local address 2: " << *addr << endl;
|
||||
if (auto addr = pc2->remoteAddress())
|
||||
cout << "Remote address 2: " << *addr << endl;
|
||||
|
||||
if (!dc1->isOpen() || !dc2->isOpen())
|
||||
throw runtime_error("DataChannel is not open");
|
||||
|
||||
pc1->close();
|
||||
pc2->close();
|
||||
|
||||
this_thread::sleep_for(1s);
|
||||
|
||||
cout << "Success" << endl;
|
||||
}
|
147
test/main.cpp
147
test/main.cpp
@ -16,144 +16,29 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "rtc/rtc.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
using namespace rtc;
|
||||
using namespace std;
|
||||
|
||||
template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
|
||||
void test_connectivity();
|
||||
void test_capi();
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
InitLogger(LogLevel::Warning);
|
||||
|
||||
Configuration config;
|
||||
// config.iceServers.emplace_back("stun:stun.l.google.com:19302");
|
||||
// config.enableIceTcp = true;
|
||||
|
||||
// TURN server example
|
||||
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
|
||||
// IceServer::RelayType::TurnUdp);
|
||||
// config.iceServers.push_back(turnServer);
|
||||
|
||||
auto pc1 = std::make_shared<PeerConnection>(config);
|
||||
auto pc2 = std::make_shared<PeerConnection>(config);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
int iResult;
|
||||
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
std::string err("WSAStartup failed. Error:");
|
||||
err.append(WSAGetLastError() + "");
|
||||
std::cout << err;
|
||||
try {
|
||||
std::cout << "*** Running connectivity test..." << std::endl;
|
||||
test_connectivity();
|
||||
std::cout << "*** Finished connectivity test" << std::endl;
|
||||
} catch (const exception &e) {
|
||||
std::cerr << "Connectivity test failed: " << e.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) {
|
||||
auto pc2 = wpc2.lock();
|
||||
if (!pc2)
|
||||
return;
|
||||
cout << "Description 1: " << sdp << endl;
|
||||
pc2->setRemoteDescription(sdp);
|
||||
});
|
||||
|
||||
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](const Candidate &candidate) {
|
||||
auto pc2 = wpc2.lock();
|
||||
if (!pc2)
|
||||
return;
|
||||
cout << "Candidate 1: " << candidate << endl;
|
||||
pc2->addRemoteCandidate(candidate);
|
||||
});
|
||||
|
||||
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
|
||||
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "Gathering state 1: " << state << endl;
|
||||
});
|
||||
|
||||
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](const Description &sdp) {
|
||||
auto pc1 = wpc1.lock();
|
||||
if (!pc1)
|
||||
return;
|
||||
cout << "Description 2: " << sdp << endl;
|
||||
pc1->setRemoteDescription(sdp);
|
||||
});
|
||||
|
||||
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](const Candidate &candidate) {
|
||||
auto pc1 = wpc1.lock();
|
||||
if (!pc1)
|
||||
return;
|
||||
cout << "Candidate 2: " << candidate << endl;
|
||||
pc1->addRemoteCandidate(candidate);
|
||||
});
|
||||
|
||||
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
|
||||
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "Gathering state 2: " << state << endl;
|
||||
});
|
||||
|
||||
shared_ptr<DataChannel> dc2;
|
||||
pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
|
||||
cout << "Got a DataChannel 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;
|
||||
}
|
||||
});
|
||||
dc2->send("Hello from 2");
|
||||
});
|
||||
|
||||
auto dc1 = pc1->createDataChannel("test");
|
||||
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
|
||||
auto dc1 = wdc1.lock();
|
||||
if (!dc1)
|
||||
return;
|
||||
cout << "DataChannel open: " << dc1->label() << 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;
|
||||
}
|
||||
});
|
||||
|
||||
this_thread::sleep_for(3s);
|
||||
|
||||
if (auto addr = pc1->localAddress())
|
||||
cout << "Local address 1: " << *addr << endl;
|
||||
if (auto addr = pc1->remoteAddress())
|
||||
cout << "Remote address 1: " << *addr << endl;
|
||||
if (auto addr = pc2->localAddress())
|
||||
cout << "Local address 2: " << *addr << endl;
|
||||
if (auto addr = pc2->remoteAddress())
|
||||
cout << "Remote address 2: " << *addr << endl;
|
||||
|
||||
if (dc1->isOpen() && dc2->isOpen()) {
|
||||
pc1->close();
|
||||
pc2->close();
|
||||
|
||||
cout << "Success" << endl;
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return 0;
|
||||
} else {
|
||||
cout << "Failure" << endl;
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
@ -21,10 +21,7 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
using namespace rtc;
|
||||
using namespace std;
|
||||
@ -36,56 +33,36 @@ int main(int argc, char **argv) {
|
||||
|
||||
Configuration config;
|
||||
// config.iceServers.emplace_back("stun.l.google.com:19302");
|
||||
// config.enableIceTcp = true;
|
||||
|
||||
// TURN Server example
|
||||
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
|
||||
// IceServer::RelayType::TurnUdp);
|
||||
// config.iceServers.push_back(turnServer);
|
||||
|
||||
auto pc = std::make_shared<PeerConnection>(config);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
int iResult;
|
||||
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
std::string err("WSAStartup failed. Error:");
|
||||
err.append(WSAGetLastError() + "");
|
||||
std::cout << err;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
pc->onLocalDescription([](const Description &sdp) {
|
||||
std::string s(sdp);
|
||||
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
|
||||
std::replace(s.begin(), s.end(), '\r', static_cast<char>(95));
|
||||
cout << "Local Description (Paste this to other peer):" << endl << s << endl << endl;
|
||||
pc->onLocalDescription([](const Description &description) {
|
||||
cout << "Local Description (Paste this to the other peer):" << endl;
|
||||
cout << string(description) << endl;
|
||||
});
|
||||
|
||||
pc->onLocalCandidate([](const Candidate &candidate) {
|
||||
cout << "Local Candidate (Paste this to other peer):" << endl << candidate << endl << endl;
|
||||
cout << "Local Candidate (Paste this to the other peer after the local description):"
|
||||
<< endl;
|
||||
cout << string(candidate) << endl << endl;
|
||||
});
|
||||
|
||||
pc->onStateChange(
|
||||
[](PeerConnection::State state) { cout << "[ State: " << state << " ]" << endl; });
|
||||
[](PeerConnection::State state) { cout << "[State: " << state << "]" << endl; });
|
||||
pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "[ Gathering State: " << state << " ]" << endl;
|
||||
cout << "[Gathering State: " << state << "]" << endl;
|
||||
});
|
||||
|
||||
shared_ptr<DataChannel> dc = nullptr;
|
||||
pc->onDataChannel([&](shared_ptr<DataChannel> _dc) {
|
||||
cout << "[ Got a DataChannel with label: " << _dc->label() << " ]" << endl;
|
||||
cout << "[Got a DataChannel with label: " << _dc->label() << "]" << endl;
|
||||
dc = _dc;
|
||||
|
||||
dc->onClosed([&]() { cout << "[ DataChannel closed: " << dc->label() << " ]" << endl; });
|
||||
dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
|
||||
|
||||
dc->onMessage([](const variant<binary, string> &message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "[ Received: " << get<string>(message) << " ]" << endl;
|
||||
cout << "[Received message: " << get<string>(message) << "]" << endl;
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -93,76 +70,65 @@ int main(int argc, char **argv) {
|
||||
bool exit = false;
|
||||
while (!exit) {
|
||||
cout << endl
|
||||
<< "**********************************************************************************"
|
||||
"*****"
|
||||
<< endl
|
||||
<< "*************************************************************************" << endl
|
||||
<< "* 0: Exit /"
|
||||
<< " 1: Enter Description /"
|
||||
<< " 2: Enter Candidate /"
|
||||
<< " 3: Send Message *" << endl
|
||||
<< " [Command]: ";
|
||||
<< " 1: Enter remote description /"
|
||||
<< " 2: Enter remote candidate /"
|
||||
<< " 3: Send message *" << endl
|
||||
<< "[Command]: ";
|
||||
|
||||
int command;
|
||||
std::string sdp, candidate, message;
|
||||
const char *a;
|
||||
std::unique_ptr<Candidate> candidatePtr;
|
||||
std::unique_ptr<Description> descPtr;
|
||||
int command = -1;
|
||||
cin >> command;
|
||||
cin.ignore();
|
||||
|
||||
switch (command) {
|
||||
case 0:
|
||||
case 0: {
|
||||
exit = true;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
}
|
||||
case 1: {
|
||||
// Parse Description
|
||||
cout << "[SDP]: ";
|
||||
sdp = "";
|
||||
while (sdp.length() == 0)
|
||||
getline(cin, sdp);
|
||||
|
||||
std::replace(sdp.begin(), sdp.end(), static_cast<char>(94), '\n');
|
||||
std::replace(sdp.begin(), sdp.end(), static_cast<char>(95), '\r');
|
||||
descPtr = std::make_unique<Description>(sdp, Description::Type::Offer,
|
||||
Description::Role::Passive);
|
||||
pc->setRemoteDescription(*descPtr);
|
||||
cout << "[Description]: ";
|
||||
string sdp, line;
|
||||
while (getline(cin, line) && !line.empty()) {
|
||||
sdp += line;
|
||||
sdp += "\r\n";
|
||||
}
|
||||
std::cout << sdp;
|
||||
pc->setRemoteDescription(sdp);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
}
|
||||
case 2: {
|
||||
// Parse Candidate
|
||||
cout << "[Candidate]: ";
|
||||
candidate = "";
|
||||
while (candidate.length() == 0)
|
||||
getline(cin, candidate);
|
||||
|
||||
candidatePtr = std::make_unique<Candidate>(candidate);
|
||||
pc->addRemoteCandidate(*candidatePtr);
|
||||
string candidate;
|
||||
getline(cin, candidate);
|
||||
pc->addRemoteCandidate(candidate);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
}
|
||||
case 3: {
|
||||
// Send Message
|
||||
if (!dc || !dc->isOpen()) {
|
||||
cout << "** Channel is not Open ** ";
|
||||
break;
|
||||
}
|
||||
cout << "[Message]: ";
|
||||
message = "";
|
||||
while (message.length() == 0)
|
||||
getline(cin, message);
|
||||
string message;
|
||||
getline(cin, message);
|
||||
dc->send(message);
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
cout << "** Invalid Command ** ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dc)
|
||||
dc->close();
|
||||
if (pc)
|
||||
pc->close();
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
@ -21,10 +21,7 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
using namespace rtc;
|
||||
using namespace std;
|
||||
@ -36,130 +33,102 @@ int main(int argc, char **argv) {
|
||||
|
||||
Configuration config;
|
||||
// config.iceServers.emplace_back("stun.l.google.com:19302");
|
||||
// config.enableIceTcp = true;
|
||||
|
||||
// TURN server example
|
||||
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
|
||||
// IceServer::RelayType::TurnUdp);
|
||||
// config.iceServers.push_back(turnServer);
|
||||
|
||||
auto pc = std::make_shared<PeerConnection>(config);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
int iResult;
|
||||
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
std::string err("WSAStartup failed. Error:");
|
||||
err.append(WSAGetLastError() + "");
|
||||
std::cout << err;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
pc->onLocalDescription([](const Description &sdp) {
|
||||
std::string s(sdp);
|
||||
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
|
||||
std::replace(s.begin(), s.end(), '\r', static_cast<char>(95));
|
||||
cout << "Local Description (Paste this to other peer):" << endl << s << endl << endl;
|
||||
pc->onLocalDescription([](const Description &description) {
|
||||
cout << "Local Description (Paste this to the other peer):" << endl;
|
||||
cout << string(description) << endl;
|
||||
});
|
||||
|
||||
pc->onLocalCandidate([](const Candidate &candidate) {
|
||||
cout << "Local Candidate (Paste this to other peer):" << endl << candidate << endl << endl;
|
||||
cout << "Local Candidate (Paste this to the other peer after the local description):"
|
||||
<< endl;
|
||||
cout << string(candidate) << endl << endl;
|
||||
});
|
||||
|
||||
pc->onStateChange(
|
||||
[](PeerConnection::State state) { cout << "[ State: " << state << " ]" << endl; });
|
||||
[](PeerConnection::State state) { cout << "[State: " << state << "]" << endl; });
|
||||
|
||||
pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
|
||||
cout << "[ Gathering State: " << state << " ]" << endl;
|
||||
cout << "[Gathering State: " << state << "]" << endl;
|
||||
});
|
||||
|
||||
auto dc = pc->createDataChannel("test");
|
||||
dc->onOpen([&]() { cout << "[ DataChannel open: " << dc->label() << " ]" << endl; });
|
||||
auto dc = pc->createDataChannel("test"); // this is the offerer, so create a data channel
|
||||
|
||||
dc->onClosed([&]() { cout << "[ DataChannel closed: " << dc->label() << " ]" << endl; });
|
||||
dc->onOpen([&]() { cout << "[DataChannel open: " << dc->label() << "]" << endl; });
|
||||
|
||||
dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
|
||||
|
||||
dc->onMessage([](const variant<binary, string> &message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "[ Received: " << get<string>(message) << " ]" << endl;
|
||||
cout << "[Received: " << get<string>(message) << "]" << endl;
|
||||
}
|
||||
});
|
||||
|
||||
this_thread::sleep_for(1s);
|
||||
|
||||
bool exit = false;
|
||||
while (!exit) {
|
||||
cout << endl
|
||||
<< "**********************************************************************************"
|
||||
"*****"
|
||||
<< endl
|
||||
<< "*************************************************************************" << endl
|
||||
<< "* 0: Exit /"
|
||||
<< " 1: Enter Description /"
|
||||
<< " 2: Enter Candidate /"
|
||||
<< " 3: Send Message *" << endl
|
||||
<< " [Command]: ";
|
||||
<< " 1: Enter remote description /"
|
||||
<< " 2: Enter remote candidate /"
|
||||
<< " 3: Send message *" << endl
|
||||
<< "[Command]: ";
|
||||
|
||||
int command;
|
||||
std::string sdp, candidate, message;
|
||||
const char *a;
|
||||
std::unique_ptr<Candidate> candidatePtr;
|
||||
std::unique_ptr<Description> descPtr;
|
||||
int command = -1;
|
||||
cin >> command;
|
||||
cin.ignore();
|
||||
|
||||
switch (command) {
|
||||
case 0:
|
||||
case 0: {
|
||||
exit = true;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
}
|
||||
case 1: {
|
||||
// Parse Description
|
||||
cout << "[SDP]: ";
|
||||
sdp = "";
|
||||
while (sdp.length() == 0)
|
||||
getline(cin, sdp);
|
||||
|
||||
std::replace(sdp.begin(), sdp.end(), static_cast<char>(94), '\n');
|
||||
std::replace(sdp.begin(), sdp.end(), static_cast<char>(95), '\r');
|
||||
descPtr = std::make_unique<Description>(sdp);
|
||||
pc->setRemoteDescription(*descPtr);
|
||||
cout << "[Description]: ";
|
||||
string sdp, line;
|
||||
while (getline(cin, line) && !line.empty()) {
|
||||
sdp += line;
|
||||
sdp += "\r\n";
|
||||
}
|
||||
pc->setRemoteDescription(sdp);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
}
|
||||
case 2: {
|
||||
// Parse Candidate
|
||||
cout << "[Candidate]: ";
|
||||
candidate = "";
|
||||
while (candidate.length() == 0)
|
||||
getline(cin, candidate);
|
||||
|
||||
candidatePtr = std::make_unique<Candidate>(candidate);
|
||||
pc->addRemoteCandidate(*candidatePtr);
|
||||
string candidate;
|
||||
getline(cin, candidate);
|
||||
pc->addRemoteCandidate(candidate);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
}
|
||||
case 3: {
|
||||
// Send Message
|
||||
if (!dc->isOpen()) {
|
||||
cout << "** Channel is not Open ** ";
|
||||
break;
|
||||
}
|
||||
cout << "[Message]: ";
|
||||
message = "";
|
||||
while (message.length() == 0)
|
||||
getline(cin, message);
|
||||
string message;
|
||||
getline(cin, message);
|
||||
dc->send(message);
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
cout << "** Invalid Command ** ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dc)
|
||||
dc->close();
|
||||
if (pc)
|
||||
pc->close();
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
Reference in New Issue
Block a user