mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 07:35:30 +00:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
8ea828f1b0 | |||
35da6636aa | |||
363827594b | |||
cbc027f144 | |||
ebc6a4b65c | |||
37d47d28a8 | |||
46878519c0 | |||
84c298f4f8 | |||
23aed2b844 | |||
df62d6d51c | |||
26241f00b7 | |||
873d14c824 | |||
4953a112ad | |||
c31e1bf0be | |||
98ddba192f | |||
b02b30eea8 | |||
324d97a9b7 | |||
0a1dd4db01 | |||
b1de9acb20 | |||
960300a7cd | |||
3f084d7527 | |||
64096d599c | |||
552e443ef1 | |||
52cb8d68a0 | |||
372e2b7a1f | |||
a92e63720c | |||
8d121c086e | |||
b538e454aa | |||
60d09d5c6f | |||
266159fe41 | |||
458decb12d | |||
635c2e5513 | |||
adc4223617 | |||
326ae27ad1 | |||
6d33d19816 | |||
734efb391a | |||
cba864507f | |||
3432b233ff | |||
ef9bfe811b | |||
949e1de9cd | |||
57c52cf7ae | |||
eaac06546e | |||
9e38d08c0b | |||
47db28617a | |||
de8c4a55cf | |||
08d94e59c7 | |||
e8a6698abd | |||
7348b2b350 | |||
a99efd27d2 |
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(libdatachannel
|
||||
DESCRIPTION "WebRTC Data Channels Library"
|
||||
VERSION 0.9.1
|
||||
VERSION 0.9.4
|
||||
LANGUAGES CXX)
|
||||
|
||||
# Options
|
||||
@ -11,6 +11,7 @@ option(NO_WEBSOCKET "Disable WebSocket support" OFF)
|
||||
option(NO_EXAMPLES "Disable examples" OFF)
|
||||
option(NO_TESTS "Disable tests build" OFF)
|
||||
option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
|
||||
option(CAPI_STDCALL "Set calling convention of C API callbacks stdcall" OFF)
|
||||
|
||||
if(USE_NICE)
|
||||
option(USE_JUICE "Use libjuice" OFF)
|
||||
@ -217,6 +218,11 @@ else()
|
||||
target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic)
|
||||
endif()
|
||||
|
||||
if(CAPI_STDCALL)
|
||||
target_compile_definitions(datachannel PUBLIC CAPI_STDCALL)
|
||||
target_compile_definitions(datachannel-static PUBLIC CAPI_STDCALL)
|
||||
endif()
|
||||
|
||||
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
|
||||
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)
|
||||
|
||||
|
@ -202,5 +202,6 @@ ws->open("wss://my.websocket/service");
|
||||
## External resources
|
||||
- Rust wrapper for libdatachannel: [datachannel-rs](https://github.com/lerouxrgd/datachannel-rs)
|
||||
- Node.js wrapper for libdatachannel: [node-datachannel](https://github.com/murat-dogan/node-datachannel)
|
||||
- Unity wrapper for Windows 10 and Hololens: [datachannel-unity](https://github.com/hanseuljun/datachannel-unity)
|
||||
- WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm)
|
||||
|
||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: da29d725ab...2111295fe8
@ -1,4 +0,0 @@
|
||||
# getopt-for-windows
|
||||
getopt.h and getopt.c is very often used in linux, to make it easy for windows user, two files were extracted from glibc. In order to make it works properly in windows, some modification was done and you may compare the change using original source files. Enjoy it!
|
||||
|
||||
Source: https://github.com/Chunde/getopt-for-windows. IMPORTANT: getopt.[ch] are likely not safe for Linux due to conflict with existing getopt.[ch]. They are thus NOT in CMakeFiles.txt and instead both files are #include only on Windows.
|
@ -66,12 +66,16 @@ int main(int argc, char **argv) {
|
||||
|
||||
Configuration config;
|
||||
string stunServer = "";
|
||||
if (params->noStun()) {
|
||||
cout << "No stun server is configured. Only local hosts and public IP addresses suported." << endl;
|
||||
} else {
|
||||
if (params->stunServer().substr(0,5).compare("stun:") != 0) {
|
||||
stunServer = "stun:";
|
||||
}
|
||||
stunServer += params->stunServer() + ":" + to_string(params->stunPort());
|
||||
cout << "Stun server is " << stunServer << endl;
|
||||
config.iceServers.emplace_back(stunServer);
|
||||
}
|
||||
|
||||
localId = randomId(4);
|
||||
cout << "The local ID is: " << localId << endl;
|
||||
|
@ -43,6 +43,8 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
|
||||
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"echo", no_argument, NULL, 'e'},
|
||||
{"noStun", no_argument, NULL, 'n'},
|
||||
{"stunServer", required_argument, NULL, 's'},
|
||||
{"stunPort", required_argument, NULL, 't'},
|
||||
{"webSocketServer", required_argument, NULL, 'w'},
|
||||
@ -55,19 +57,28 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
|
||||
_program_name += argv[0];
|
||||
|
||||
/* default values */
|
||||
_e = false;
|
||||
_n = false;
|
||||
_s = "stun.l.google.com";
|
||||
_t = 19302;
|
||||
_w = "localhost";
|
||||
_x = 8000;
|
||||
_e = false;
|
||||
_h = false;
|
||||
_v = false;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt_long (argc, argv, "s:t:w:x:ehv", long_options, &optind)) != - 1)
|
||||
while ((c = getopt_long (argc, argv, "s:t:w:x:enhv", long_options, &optind)) != - 1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'e':
|
||||
_e = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
_n = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
_s = optarg;
|
||||
break;
|
||||
@ -108,10 +119,6 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
|
||||
}
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
_e = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
_h = true;
|
||||
this->usage (EXIT_SUCCESS);
|
||||
@ -146,10 +153,12 @@ void Cmdline::usage (int status)
|
||||
else
|
||||
{
|
||||
std::cout << "\
|
||||
usage: " << _program_name << " [ -estwxhv ] \n\
|
||||
usage: " << _program_name << " [ -enstwxhv ] \n\
|
||||
libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\
|
||||
[ -e ] [ --echo ] (type=FLAG)\n\
|
||||
Echo data channel messages back to sender rather than putting to stdout.\n\
|
||||
[ -n ] [ --noStun ] (type=FLAG)\n\
|
||||
Do NOT use a stun server (overrides -s and -t).\n\
|
||||
[ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\
|
||||
Stun server URL or IP address.\n\
|
||||
[ -t ] [ --stunPort ] (type=INTEGER, range=0...65535, default=19302)\n\
|
||||
|
@ -34,11 +34,12 @@ class Cmdline
|
||||
{
|
||||
private:
|
||||
/* parameters */
|
||||
bool _e;
|
||||
bool _n;
|
||||
std::string _s;
|
||||
int _t;
|
||||
std::string _w;
|
||||
int _x;
|
||||
bool _e;
|
||||
bool _h;
|
||||
bool _v;
|
||||
|
||||
@ -60,11 +61,12 @@ public:
|
||||
/* return next (non-option) parameter */
|
||||
int next_param () { return _optind; }
|
||||
|
||||
bool echoDataChannelMessages () const { return _e; }
|
||||
bool noStun () const { return _n; }
|
||||
std::string stunServer () const { return _s; }
|
||||
int stunPort () const { return _t; }
|
||||
std::string webSocketServer () const { return _w; }
|
||||
int webSocketPort () const { return _x; }
|
||||
bool echoDataChannelMessages () const { return _e; }
|
||||
bool h () const { return _h; }
|
||||
bool v () const { return _v; }
|
||||
};
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
~Queue();
|
||||
|
||||
void stop();
|
||||
bool running() const;
|
||||
bool empty() const;
|
||||
bool full() const;
|
||||
size_t size() const; // elements
|
||||
@ -80,6 +81,11 @@ template <typename T> void Queue<T>::stop() {
|
||||
mPushCondition.notify_all();
|
||||
}
|
||||
|
||||
template <typename T> bool Queue<T>::running() const {
|
||||
std::lock_guard lock(mMutex);
|
||||
return !mQueue.empty() || !mStopping;
|
||||
}
|
||||
|
||||
template <typename T> bool Queue<T>::empty() const {
|
||||
std::lock_guard lock(mMutex);
|
||||
return mQueue.empty();
|
||||
|
@ -29,6 +29,12 @@ extern "C" {
|
||||
#define RTC_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef CAPI_STDCALL
|
||||
#define RTC_API __stdcall
|
||||
#else
|
||||
#define RTC_API
|
||||
#endif
|
||||
|
||||
#ifndef RTC_ENABLE_WEBSOCKET
|
||||
#define RTC_ENABLE_WEBSOCKET 1
|
||||
#endif
|
||||
@ -81,22 +87,23 @@ typedef struct {
|
||||
unsigned int maxRetransmits; // ignored if reliable
|
||||
} rtcReliability;
|
||||
|
||||
typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
|
||||
typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
|
||||
typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
|
||||
typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
|
||||
typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
|
||||
typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
|
||||
typedef void (*rtcTrackCallbackFunc)(int tr, void *ptr);
|
||||
typedef void (*rtcOpenCallbackFunc)(void *ptr);
|
||||
typedef void (*rtcClosedCallbackFunc)(void *ptr);
|
||||
typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
|
||||
typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
|
||||
typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr);
|
||||
typedef void (*rtcAvailableCallbackFunc)(void *ptr);
|
||||
typedef void (RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
|
||||
typedef void (RTC_API *rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
|
||||
typedef void (RTC_API *rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
|
||||
typedef void (RTC_API *rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
|
||||
typedef void (RTC_API *rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
|
||||
typedef void (RTC_API *rtcDataChannelCallbackFunc)(int dc, void *ptr);
|
||||
typedef void (RTC_API *rtcTrackCallbackFunc)(int tr, void *ptr);
|
||||
typedef void (RTC_API *rtcOpenCallbackFunc)(void *ptr);
|
||||
typedef void (RTC_API *rtcClosedCallbackFunc)(void *ptr);
|
||||
typedef void (RTC_API *rtcErrorCallbackFunc)(const char *error, void *ptr);
|
||||
typedef void (RTC_API *rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
|
||||
typedef void (RTC_API *rtcBufferedAmountLowCallbackFunc)(void *ptr);
|
||||
typedef void (RTC_API *rtcAvailableCallbackFunc)(void *ptr);
|
||||
|
||||
// Log
|
||||
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); // NULL cb to log to stdout
|
||||
// NULL cb on the first call will log to stdout
|
||||
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb);
|
||||
|
||||
// User pointer
|
||||
RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
|
||||
@ -114,6 +121,9 @@ RTC_EXPORT int rtcSetLocalDescription(int pc);
|
||||
RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
|
||||
RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
|
||||
|
||||
RTC_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size);
|
||||
RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
|
||||
|
||||
RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
|
||||
RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
|
||||
|
||||
|
@ -94,7 +94,7 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
|
||||
struct addrinfo *result = nullptr;
|
||||
if (getaddrinfo(node.c_str(), service.c_str(), &hints, &result) == 0) {
|
||||
for (auto p = result; p; p = p->ai_next)
|
||||
for (auto p = result; p; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
|
||||
// Rewrite the candidate
|
||||
char nodebuffer[MAX_NUMERICNODE_LEN];
|
||||
@ -117,6 +117,7 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
}
|
||||
|
||||
return mIsResolved;
|
||||
}
|
||||
|
85
src/capi.cpp
85
src/capi.cpp
@ -195,12 +195,17 @@ template <typename F> int wrap(F func) {
|
||||
return RTC_ERR_SUCCESS; \
|
||||
})
|
||||
|
||||
class plog_appender : public plog::IAppender {
|
||||
class plogAppender : public plog::IAppender {
|
||||
public:
|
||||
plog_appender(rtcLogCallbackFunc cb = nullptr) { set_callback(cb); }
|
||||
plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
|
||||
|
||||
void set_callback(rtcLogCallbackFunc cb) {
|
||||
std::lock_guard lock(mutex);
|
||||
plogAppender(plogAppender &&appender) : callback(nullptr) {
|
||||
std::lock_guard lock(appender.callbackMutex);
|
||||
std::swap(appender.callback, callback);
|
||||
}
|
||||
|
||||
void setCallback(rtcLogCallbackFunc cb) {
|
||||
std::lock_guard lock(callbackMutex);
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
@ -215,7 +220,7 @@ public:
|
||||
#else
|
||||
std::string str = formatted;
|
||||
#endif
|
||||
std::lock_guard lock(mutex);
|
||||
std::lock_guard lock(callbackMutex);
|
||||
if (callback)
|
||||
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
|
||||
else
|
||||
@ -224,18 +229,24 @@ public:
|
||||
|
||||
private:
|
||||
rtcLogCallbackFunc callback;
|
||||
std::mutex callbackMutex;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
|
||||
static std::optional<plog_appender> appender;
|
||||
if (appender)
|
||||
appender->set_callback(cb);
|
||||
else if (cb)
|
||||
appender.emplace(plog_appender(cb));
|
||||
|
||||
InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr);
|
||||
static std::optional<plogAppender> appender;
|
||||
const auto severity = static_cast<plog::Severity>(level);
|
||||
std::lock_guard lock(mutex);
|
||||
if (appender) {
|
||||
appender->setCallback(cb);
|
||||
InitLogger(severity, nullptr); // change the severity
|
||||
} else if (cb) {
|
||||
appender.emplace(plogAppender(cb));
|
||||
InitLogger(severity, &appender.value());
|
||||
} else {
|
||||
InitLogger(severity, nullptr); // log to stdout
|
||||
}
|
||||
}
|
||||
|
||||
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
|
||||
@ -518,6 +529,52 @@ int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetLocalDescription(int pc, char *buffer, int size) {
|
||||
return WRAP({
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
|
||||
if (size <= 0)
|
||||
return 0;
|
||||
|
||||
if (!buffer)
|
||||
throw std::invalid_argument("Unexpected null pointer for buffer");
|
||||
|
||||
if (auto desc = peerConnection->localDescription()) {
|
||||
auto sdp = string(*desc);
|
||||
const char *data = sdp.data();
|
||||
size = std::min(size - 1, int(sdp.size()));
|
||||
std::copy(data, data + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
return RTC_ERR_FAILURE;
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetRemoteDescription(int pc, char *buffer, int size) {
|
||||
return WRAP({
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
|
||||
if (size <= 0)
|
||||
return 0;
|
||||
|
||||
if (!buffer)
|
||||
throw std::invalid_argument("Unexpected null pointer for buffer");
|
||||
|
||||
if (auto desc = peerConnection->remoteDescription()) {
|
||||
auto sdp = string(*desc);
|
||||
const char *data = sdp.data();
|
||||
size = std::min(size - 1, int(sdp.size()));
|
||||
std::copy(data, data + size, buffer);
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
return RTC_ERR_FAILURE;
|
||||
});
|
||||
}
|
||||
|
||||
int rtcGetLocalAddress(int pc, char *buffer, int size) {
|
||||
return WRAP({
|
||||
auto peerConnection = getPeerConnection(pc);
|
||||
@ -535,6 +592,8 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
|
||||
buffer[size] = '\0';
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
return RTC_ERR_FAILURE;
|
||||
});
|
||||
}
|
||||
|
||||
@ -555,6 +614,8 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
|
||||
buffer[size] = '\0';
|
||||
return int(size + 1);
|
||||
}
|
||||
|
||||
return RTC_ERR_FAILURE;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -218,6 +218,8 @@ void DataChannel::incoming(message_ptr message) {
|
||||
|
||||
switch (message->type) {
|
||||
case Message::Control: {
|
||||
if (message->size() == 0)
|
||||
break; // Ignore
|
||||
auto raw = reinterpret_cast<const uint8_t *>(message->data());
|
||||
switch (raw[0]) {
|
||||
case MESSAGE_OPEN:
|
||||
|
@ -435,7 +435,7 @@ void DtlsTransport::runRecvLoop() {
|
||||
|
||||
const size_t bufferSize = maxMtu;
|
||||
byte buffer[bufferSize];
|
||||
while (true) {
|
||||
while (mIncomingQueue.running()) {
|
||||
// Process pending messages
|
||||
while (auto next = mIncomingQueue.tryPop()) {
|
||||
message_ptr message = std::move(*next);
|
||||
@ -492,8 +492,7 @@ void DtlsTransport::runRecvLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!mIncomingQueue.wait(duration))
|
||||
break; // queue is stopped
|
||||
mIncomingQueue.wait(duration);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
PLOG_ERROR << "DTLS recv: " << e.what();
|
||||
|
@ -60,7 +60,8 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
||||
}
|
||||
|
||||
juice_log_level_t level;
|
||||
switch (plog::get()->getMaxSeverity()) {
|
||||
auto logger = plog::get();
|
||||
switch (logger ? logger->getMaxSeverity() : plog::none) {
|
||||
case plog::none:
|
||||
level = JUICE_LOG_LEVEL_NONE;
|
||||
break;
|
||||
@ -380,8 +381,11 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
struct addrinfo *result = nullptr;
|
||||
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0)
|
||||
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
|
||||
PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
|
||||
<< server.service;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto p = result; p; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET) {
|
||||
@ -423,8 +427,11 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
||||
server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
struct addrinfo *result = nullptr;
|
||||
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0)
|
||||
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
|
||||
PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
|
||||
<< server.service;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto p = result; p; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "plog/Log.h"
|
||||
#include "plog/Logger.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace rtc {
|
||||
|
||||
void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
|
||||
@ -31,6 +33,8 @@ void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level))
|
||||
void InitLogger(plog::Severity severity, plog::IAppender *appender) {
|
||||
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
|
||||
static plog::Logger<0> *logger = nullptr;
|
||||
static std::mutex mutex;
|
||||
std::lock_guard lock(mutex);
|
||||
if (!logger) {
|
||||
logger = &plog::init(severity, appender ? appender : &consoleAppender);
|
||||
PLOG_DEBUG << "Logger initialized";
|
||||
|
@ -102,6 +102,7 @@ void PeerConnection::setLocalDescription() {
|
||||
|
||||
if (std::atomic_load(&mIceTransport)) {
|
||||
PLOG_DEBUG << "Local description is already set, ignoring";
|
||||
return;
|
||||
}
|
||||
|
||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||
@ -546,17 +547,17 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
||||
}
|
||||
|
||||
auto channel = findDataChannel(uint16_t(message->stream));
|
||||
|
||||
if (!channel) {
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||
if (!iceTransport || !sctpTransport)
|
||||
return;
|
||||
|
||||
if (!channel) {
|
||||
const byte dataChannelOpenMessage{0x03};
|
||||
unsigned int remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
|
||||
if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
|
||||
message->stream % 2 == remoteParity) {
|
||||
|
||||
channel =
|
||||
std::make_shared<DataChannel>(shared_from_this(), sctpTransport, message->stream);
|
||||
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
|
||||
|
56
src/rtcp.cpp
56
src/rtcp.cpp
@ -71,24 +71,24 @@ private:
|
||||
uint32_t _delaySinceLastReport;
|
||||
|
||||
public:
|
||||
inline void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost,
|
||||
inline void preparePacket(SSRC ssrc_, [[maybe_unused]] unsigned int packetsLost,
|
||||
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
|
||||
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
|
||||
uint64_t lastSR_DELAY) {
|
||||
setSeqNo(highestSeqNo, seqNoCycles);
|
||||
setJitter(jitter);
|
||||
setSSRC(ssrc);
|
||||
setSSRC(ssrc_);
|
||||
|
||||
// Middle 32 bits of NTP Timestamp
|
||||
// this->lastReport = lastSR_NTP >> 16u;
|
||||
// _lastReport = lastSR_NTP >> 16u;
|
||||
setNTPOfSR(uint32_t(lastSR_NTP));
|
||||
setDelaySinceSR(uint32_t(lastSR_DELAY));
|
||||
|
||||
// The delay, expressed in units of 1/65536 seconds
|
||||
// this->delaySinceLastReport = lastSR_DELAY;
|
||||
// _delaySinceLastReport = lastSR_DELAY;
|
||||
}
|
||||
|
||||
inline void setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); }
|
||||
inline void setSSRC(SSRC ssrc_) { ssrc = htonl(ssrc_); }
|
||||
inline SSRC getSSRC() const { return ntohl(ssrc); }
|
||||
|
||||
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
|
||||
@ -172,7 +172,7 @@ public:
|
||||
|
||||
struct RTCP_SR {
|
||||
RTCP_HEADER header;
|
||||
SSRC senderSSRC;
|
||||
SSRC senderSsrc;
|
||||
|
||||
private:
|
||||
uint64_t _ntpTimestamp;
|
||||
@ -183,11 +183,11 @@ private:
|
||||
RTCP_ReportBlock _reportBlocks;
|
||||
|
||||
public:
|
||||
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
|
||||
inline void preparePacket(SSRC senderSsrc_, uint8_t reportCount) {
|
||||
unsigned int length =
|
||||
((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
|
||||
header.prepareHeader(200, reportCount, uint16_t(length));
|
||||
this->senderSSRC = htonl(senderSSRC);
|
||||
senderSsrc = htonl(senderSsrc_);
|
||||
}
|
||||
|
||||
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
||||
@ -209,7 +209,7 @@ public:
|
||||
inline void log() const {
|
||||
header.log();
|
||||
PLOG_DEBUG << "RTCP SR: "
|
||||
<< " SSRC=" << ntohl(senderSSRC) << ", NTP_TS=" << ntpTimestamp()
|
||||
<< " SSRC=" << ntohl(senderSsrc) << ", NTP_TS=" << ntpTimestamp()
|
||||
<< ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
|
||||
<< ", octetCount=" << octetCount();
|
||||
|
||||
@ -221,7 +221,7 @@ public:
|
||||
|
||||
struct RTCP_RR {
|
||||
RTCP_HEADER header;
|
||||
SSRC senderSSRC;
|
||||
SSRC senderSsrc;
|
||||
|
||||
private:
|
||||
RTCP_ReportBlock _reportBlocks;
|
||||
@ -230,19 +230,19 @@ public:
|
||||
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
||||
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
|
||||
|
||||
inline SSRC getSenderSSRC() const { return ntohl(senderSSRC); }
|
||||
inline void setSenderSSRC(SSRC ssrc) { this->senderSSRC = htonl(ssrc); }
|
||||
inline SSRC getSenderSSRC() const { return ntohl(senderSsrc); }
|
||||
inline void setSenderSSRC(SSRC ssrc) { senderSsrc = htonl(ssrc); }
|
||||
|
||||
[[nodiscard]] inline size_t getSize() const {
|
||||
// "length" in packet is one less than the number of 32 bit words in the packet.
|
||||
return sizeof(uint32_t) * (1 + size_t(header.length()));
|
||||
}
|
||||
|
||||
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
|
||||
inline void preparePacket(SSRC ssrc, uint8_t reportCount) {
|
||||
// "length" in packet is one less than the number of 32 bit words in the packet.
|
||||
size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1;
|
||||
header.prepareHeader(201, reportCount, uint16_t(length));
|
||||
this->senderSSRC = htonl(senderSSRC);
|
||||
senderSsrc = htonl(ssrc);
|
||||
}
|
||||
|
||||
inline static size_t sizeWithReportBlocks(uint8_t reportCount) {
|
||||
@ -252,7 +252,7 @@ public:
|
||||
inline void log() const {
|
||||
header.log();
|
||||
PLOG_DEBUG << "RTCP RR: "
|
||||
<< " SSRC=" << ntohl(senderSSRC);
|
||||
<< " SSRC=" << ntohl(senderSsrc);
|
||||
|
||||
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
|
||||
getReportBlock(i)->log();
|
||||
@ -262,7 +262,7 @@ public:
|
||||
|
||||
struct RTCP_REMB {
|
||||
RTCP_HEADER header;
|
||||
SSRC senderSSRC;
|
||||
SSRC senderSsrc;
|
||||
SSRC mediaSourceSSRC;
|
||||
|
||||
// Unique identifier
|
||||
@ -278,48 +278,48 @@ struct RTCP_REMB {
|
||||
return sizeof(uint32_t) * (1 + size_t(header.length()));
|
||||
}
|
||||
|
||||
inline void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
|
||||
inline void preparePacket(SSRC senderSsrc_, unsigned int numSSRC, unsigned int br) {
|
||||
// Report Count becomes the format here.
|
||||
header.prepareHeader(206, 15, 0);
|
||||
|
||||
// Always zero.
|
||||
mediaSourceSSRC = 0;
|
||||
|
||||
this->senderSSRC = htonl(senderSSRC);
|
||||
setBitrate(numSSRC, bitrate);
|
||||
senderSsrc = htonl(senderSsrc_);
|
||||
setBitrate(numSSRC, br);
|
||||
}
|
||||
|
||||
inline void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
|
||||
inline void setBitrate(unsigned int numSSRC, unsigned int br) {
|
||||
unsigned int exp = 0;
|
||||
while (bitrate > pow(2, 18) - 1) {
|
||||
while (br > pow(2, 18) - 1) {
|
||||
exp++;
|
||||
bitrate /= 2;
|
||||
br /= 2;
|
||||
}
|
||||
|
||||
// "length" in packet is one less than the number of 32 bit words in the packet.
|
||||
header.setLength(uint16_t(((sizeof(header) + 4 * 2 + 4 + 4) / 4) - 1 + numSSRC));
|
||||
|
||||
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate);
|
||||
bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | br);
|
||||
}
|
||||
|
||||
// TODO Make this work
|
||||
// uint64_t getBitrate() const{
|
||||
// uint32_t ntohed = ntohl(this->bitrate);
|
||||
// uint32_t ntohed = ntohl(bitrate);
|
||||
// uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1);
|
||||
// unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u));
|
||||
// return bitrate * pow(2,exp);
|
||||
// }
|
||||
//
|
||||
// uint8_t getNumSSRCS() const {
|
||||
// return ntohl(this->bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
|
||||
// return ntohl(bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
|
||||
// }
|
||||
|
||||
inline void setSSRC(uint8_t iterator, SSRC ssrc) { this->ssrc[iterator] = htonl(ssrc); }
|
||||
inline void setSSRC(uint8_t iterator, SSRC ssrc_) { ssrc[iterator] = htonl(ssrc_); }
|
||||
|
||||
inline void log() const {
|
||||
header.log();
|
||||
PLOG_DEBUG << "RTCP REMB: "
|
||||
<< " SSRC=" << ntohl(senderSSRC);
|
||||
<< " SSRC=" << ntohl(senderSsrc);
|
||||
}
|
||||
|
||||
static unsigned int sizeWithSSRCs(int numSSRC) {
|
||||
@ -407,7 +407,7 @@ void RtcpSession::pushRR(unsigned int lastSR_delay) {
|
||||
auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
|
||||
auto rr = reinterpret_cast<RTCP_RR *>(msg->data());
|
||||
rr->preparePacket(mSsrc, 1);
|
||||
rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, mGreatestSeqNo, 0, 0, mSyncNTPTS,
|
||||
rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, uint16_t(mGreatestSeqNo), 0, 0, mSyncNTPTS,
|
||||
lastSR_delay);
|
||||
rr->log();
|
||||
|
||||
|
@ -476,8 +476,6 @@ int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*a
|
||||
const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
|
||||
try {
|
||||
PLOG_VERBOSE << "Handle recv, len=" << len;
|
||||
if (!len)
|
||||
return 0; // Ignore
|
||||
|
||||
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
||||
// therefore partial notifications and messages need to be handled separately.
|
||||
@ -497,7 +495,7 @@ int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*a
|
||||
if (flags & MSG_EOR) {
|
||||
// Message is complete, process it
|
||||
processData(std::move(mPartialMessage), info.rcv_sid,
|
||||
PayloadId(htonl(info.rcv_ppid)));
|
||||
PayloadId(ntohl(info.rcv_ppid)));
|
||||
mPartialMessage.clear();
|
||||
}
|
||||
}
|
||||
|
@ -51,31 +51,45 @@ WebSocket::~WebSocket() {
|
||||
WebSocket::State WebSocket::readyState() const { return mState; }
|
||||
|
||||
void WebSocket::open(const string &url) {
|
||||
PLOG_VERBOSE << "Opening WebSocket to URL: " << url;
|
||||
|
||||
if (mState != State::Closed)
|
||||
throw std::runtime_error("WebSocket must be closed before opening");
|
||||
throw std::logic_error("WebSocket must be closed before opening");
|
||||
|
||||
static const char *rs = R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)";
|
||||
static std::regex regex(rs, std::regex::extended);
|
||||
// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
|
||||
static const char *rs =
|
||||
R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
|
||||
|
||||
std::smatch match;
|
||||
if (!std::regex_match(url, match, regex))
|
||||
throw std::invalid_argument("Malformed WebSocket URL: " + url);
|
||||
static const std::regex r(rs, std::regex::extended);
|
||||
|
||||
mScheme = match[2];
|
||||
if (mScheme != "ws" && mScheme != "wss")
|
||||
std::smatch m;
|
||||
if (!std::regex_match(url, m, r) || m[10].length() == 0)
|
||||
throw std::invalid_argument("Invalid WebSocket URL: " + url);
|
||||
|
||||
mScheme = m[2];
|
||||
if (mScheme.empty())
|
||||
mScheme = "ws";
|
||||
else if (mScheme != "ws" && mScheme != "wss")
|
||||
throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme);
|
||||
|
||||
mHost = match[4];
|
||||
if (auto pos = mHost.find(':'); pos != string::npos) {
|
||||
mHostname = mHost.substr(0, pos);
|
||||
mService = mHost.substr(pos + 1);
|
||||
} else {
|
||||
mHostname = mHost;
|
||||
mHostname = m[10];
|
||||
mService = m[12];
|
||||
if (mService.empty()) {
|
||||
mService = mScheme == "ws" ? "80" : "443";
|
||||
mHost = mHostname;
|
||||
} else {
|
||||
mHost = mHostname + ':' + mService;
|
||||
}
|
||||
|
||||
mPath = match[5];
|
||||
if (string query = match[7]; !query.empty())
|
||||
while (!mHostname.empty() && mHostname.front() == '[')
|
||||
mHostname.erase(mHostname.begin());
|
||||
while (!mHostname.empty() && mHostname.back() == ']')
|
||||
mHostname.pop_back();
|
||||
|
||||
mPath = m[13];
|
||||
if (mPath.empty())
|
||||
mPath += '/';
|
||||
if (string query = m[15]; !query.empty())
|
||||
mPath += "?" + query;
|
||||
|
||||
changeState(State::Connecting);
|
||||
@ -133,6 +147,11 @@ bool WebSocket::outgoing(message_ptr message) {
|
||||
}
|
||||
|
||||
void WebSocket::incoming(message_ptr message) {
|
||||
if (!message) {
|
||||
remoteClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->type == Message::String || message->type == Message::Binary) {
|
||||
mRecvQueue.push(message);
|
||||
triggerAvailable(mRecvQueue.size());
|
||||
|
@ -58,6 +58,12 @@ WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string p
|
||||
onRecv(recvCallback);
|
||||
|
||||
PLOG_DEBUG << "Initializing WebSocket transport";
|
||||
|
||||
if (mHost.empty())
|
||||
throw std::invalid_argument("WebSocket HTTP host cannot be empty");
|
||||
|
||||
if (mPath.empty())
|
||||
throw std::invalid_argument("WebSocket HTTP path cannot be empty");
|
||||
}
|
||||
|
||||
WsTransport::~WsTransport() { stop(); }
|
||||
@ -147,7 +153,7 @@ void WsTransport::close() {
|
||||
}
|
||||
|
||||
bool WsTransport::sendHttpRequest() {
|
||||
PLOG_DEBUG << "Sending WebSocket HTTP request";
|
||||
PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mPath;
|
||||
changeState(State::Connecting);
|
||||
|
||||
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||
|
@ -29,6 +29,8 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
rtcState state;
|
||||
rtcGatheringState gatheringState;
|
||||
@ -183,14 +185,54 @@ int test_capi_connectivity_main() {
|
||||
goto error;
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
if (rtcGetLocalAddress(peer1->pc, buffer, 256) >= 0)
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
if (rtcGetLocalDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetLocalDescription failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Local description 1: %s\n", buffer);
|
||||
|
||||
if (rtcGetRemoteDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetRemoteDescription failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Remote description 1: %s\n", buffer);
|
||||
|
||||
if (rtcGetLocalDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetLocalDescription failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Local description 2: %s\n", buffer);
|
||||
|
||||
if (rtcGetRemoteDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetRemoteDescription failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Remote description 2: %s\n", buffer);
|
||||
|
||||
if (rtcGetLocalAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetLocalAddress failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Local address 1: %s\n", buffer);
|
||||
if (rtcGetRemoteAddress(peer1->pc, buffer, 256) >= 0)
|
||||
|
||||
if (rtcGetRemoteAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetRemoteAddress failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Remote address 1: %s\n", buffer);
|
||||
if (rtcGetLocalAddress(peer2->pc, buffer, 256) >= 0)
|
||||
|
||||
if (rtcGetLocalAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetLocalAddress failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Local address 2: %s\n", buffer);
|
||||
if (rtcGetRemoteAddress(peer2->pc, buffer, 256) >= 0)
|
||||
|
||||
if (rtcGetRemoteAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
|
||||
fprintf(stderr, "rtcGetRemoteAddress failed\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Remote address 2: %s\n", buffer);
|
||||
|
||||
deletePeer(peer1);
|
||||
|
@ -125,6 +125,7 @@ void test_connectivity() {
|
||||
}
|
||||
});
|
||||
|
||||
// Wait a bit
|
||||
int attempts = 10;
|
||||
shared_ptr<DataChannel> adc2;
|
||||
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
|
||||
@ -146,6 +147,49 @@ void test_connectivity() {
|
||||
if (auto addr = pc2->remoteAddress())
|
||||
cout << "Remote address 2: " << *addr << endl;
|
||||
|
||||
// Try to open a second data channel with another label
|
||||
shared_ptr<DataChannel> second2;
|
||||
pc2->onDataChannel([&second2](shared_ptr<DataChannel> dc) {
|
||||
cout << "Second DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
|
||||
if (dc->label() != "second") {
|
||||
cerr << "Wrong second DataChannel label" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
dc->onMessage([](variant<binary, string> message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "Second Message 2: " << get<string>(message) << endl;
|
||||
}
|
||||
});
|
||||
|
||||
dc->send("Send hello from 2");
|
||||
|
||||
std::atomic_store(&second2, dc);
|
||||
});
|
||||
|
||||
auto second1 = pc1->createDataChannel("second");
|
||||
second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() {
|
||||
auto second1 = wsecond1.lock();
|
||||
if (!second1)
|
||||
return;
|
||||
|
||||
cout << "Second DataChannel 1: Open" << endl;
|
||||
second1->send("Second hello from 1");
|
||||
});
|
||||
dc1->onMessage([](const variant<binary, string> &message) {
|
||||
if (holds_alternative<string>(message)) {
|
||||
cout << "Second Message 1: " << get<string>(message) << endl;
|
||||
}
|
||||
});
|
||||
|
||||
// Wait a bit
|
||||
attempts = 10;
|
||||
shared_ptr<DataChannel> asecond2;
|
||||
while (
|
||||
(!(asecond2 = std::atomic_load(&second2)) || !asecond2->isOpen() || !second1->isOpen()) &&
|
||||
attempts--)
|
||||
this_thread::sleep_for(1s);
|
||||
|
||||
// Delay close of peer 2 to check closing works properly
|
||||
pc1->close();
|
||||
this_thread::sleep_for(1s);
|
||||
|
Reference in New Issue
Block a user