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