Compare commits

...

32 Commits

Author SHA1 Message Date
679ecfc066 Bumped version to 0.10.5 2021-01-21 14:40:21 +01:00
4e3ea69073 Join thread pool at exit to prevent static destruction order issue 2021-01-21 12:08:57 +01:00
b79c886480 Force BUILD_SHARED_LIBS to OFF to build usrsctp as static 2021-01-20 20:27:01 +01:00
08da691911 Added RTC defines to usage requirement in Jamfile 2021-01-20 20:27:01 +01:00
754568506a Fixed implicit conversion of stream from unsigned int to uint16_t 2021-01-20 20:27:01 +01:00
7ac351d1b9 Fixed local variable shadowing 2021-01-20 20:27:01 +01:00
6d5cecbca1 Fixed usrsctp build in Jamfile 2021-01-20 20:27:01 +01:00
abec5fc219 Bumped version to 0.10.4 2020-11-29 19:15:54 +01:00
684b7ba925 Merge pull request #280 from paullouisageneau/default-data-mid
Change default data mid
2020-11-29 17:33:20 +01:00
d8515b6362 Some cleanup 2020-11-29 17:24:20 +01:00
62da885028 Changed default data mid to a number 2020-11-29 17:13:55 +01:00
ff2e83bbdc Call rtcAddRemoteCandidate() with NULL mid 2020-11-29 17:06:19 +01:00
8f9e8e718e Uncluttered client example to keep it simple 2020-11-29 17:02:01 +01:00
c6bee7b0d4 Added console logging in web example 2020-11-29 17:00:43 +01:00
4d93303be8 Updated libjuice to v0.6.2 2020-11-29 16:26:31 +01:00
1620ddfb03 Merge pull request #279 from paullouisageneau/fix-candidates-mid
Fix mid on local candidates
2020-11-29 16:25:50 +01:00
452b742adc Fixed mid on local candidates 2020-11-29 16:03:57 +01:00
244c834992 Cleanup and reformatting 2020-11-28 17:03:51 +01:00
ffe202a6a2 Merge pull request #278 from hhgyu/add-support-protocols
support Subprotocols
2020-11-28 13:18:06 +01:00
ea87e5ae09 Exposed send(data, size) on Channel 2020-11-27 21:20:24 +01:00
4259b4e968 fixed build failed for windows 2020-11-27 12:22:22 +09:00
6aff5dc5bc fixed build failed for macos 2020-11-27 12:11:16 +09:00
99bae7f830 support WebSocket Protocol RFC6455 2020-11-27 11:15:37 +09:00
7598d992dc Merge pull request #274 from stazio/newdesc
Exposed Additional rtc::Description::Media calls
2020-11-25 22:24:07 +01:00
6380038584 Merge pull request #277 from paullouisageneau/fix-websocket-case
Fix WebSocket Upgrade header check
2020-11-25 22:23:18 +01:00
6144bca0f7 Changed update header check to be case-insensitive 2020-11-25 20:46:02 +01:00
6ec129f8f8 Re-Formatting to use spaces 2020-11-23 20:37:29 -05:00
be394b7185 Cleaned up addVideoCodec 2020-11-23 20:33:26 -05:00
f008b5b447 Merge remote-tracking branch 'paulgit/master' into newdesc 2020-11-23 20:22:03 -05:00
5482912e18 Merge pull request #273 from stazio/fix_rtp_protect
Fixed a typo during the protection of RTP packets
2020-11-23 22:59:29 +01:00
fcc4eaf78b Fixed a typo during the protection of RTP packets 2020-11-23 15:55:36 -05:00
cca0742973 Added some more description options 2020-11-23 15:54:01 -05:00
29 changed files with 260 additions and 222 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
project(libdatachannel project(libdatachannel
VERSION 0.10.3 VERSION 0.10.5
LANGUAGES CXX) LANGUAGES CXX)
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library") set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
@ -33,6 +33,7 @@ endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
set(BUILD_SHARED_LIBS OFF) # to force usrsctp to be built static
if(WIN32) if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN) add_definitions(-DWIN32_LEAN_AND_MEAN)

View File

@ -15,9 +15,9 @@ lib libdatachannel
: # requirements : # requirements
<cxxstd>17 <cxxstd>17
<include>./include/rtc <include>./include/rtc
<define>USE_NICE=0
<define>RTC_ENABLE_MEDIA=0 <define>RTC_ENABLE_MEDIA=0
<define>RTC_ENABLE_WEBSOCKET=0 <define>RTC_ENABLE_WEBSOCKET=0
<define>USE_NICE=0
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN <toolset>msvc:<define>WIN32_LEAN_AND_MEAN
<toolset>msvc:<define>NOMINMAX <toolset>msvc:<define>NOMINMAX
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
@ -32,6 +32,8 @@ lib libdatachannel
<link>static <link>static
: # usage requirements : # usage requirements
<include>./include <include>./include
<define>RTC_ENABLE_MEDIA=0
<define>RTC_ENABLE_WEBSOCKET=0
<library>/libdatachannel//plog <library>/libdatachannel//plog
<toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable" <toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
<toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable" <toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
@ -94,7 +96,7 @@ rule make_libusrsctp ( targets * : sources * : properties * )
} }
actions make_libusrsctp actions make_libusrsctp
{ {
(cd $(CWD)/deps/usrsctp && mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(VARIANT) -DCMAKE_C_FLAGS="-fPIC -Wno-unknown-warning-option -Wno-format-truncation" .. && make -j2 usrsctp-static) (cd $(CWD)/deps/usrsctp && mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(VARIANT) -DCMAKE_C_FLAGS="-fPIC -Wno-unknown-warning-option -Wno-format-truncation" -Dsctp_build_shared_lib=0 -Dsctp_build_programs=0 .. && make -j2 usrsctp)
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/libusrsctp.a $(<) cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/libusrsctp.a $(<)
} }
rule make_libusrsctp_msvc ( targets * : sources * : properties * ) rule make_libusrsctp_msvc ( targets * : sources * : properties * )
@ -109,7 +111,7 @@ actions make_libusrsctp_msvc
cd $(CWD)/deps/usrsctp cd $(CWD)/deps/usrsctp
mkdir $(BUILD_DIR) mkdir $(BUILD_DIR)
cd $(BUILD_DIR) cd $(BUILD_DIR)
cmake -G "Visual Studio 16 2019" .. cmake -G "Visual Studio 16 2019" -Dsctp_build_shared_lib=0 -Dsctp_build_programs=0 ..
msbuild usrsctplib.sln /property:Configuration=$(VARIANT) msbuild usrsctplib.sln /property:Configuration=$(VARIANT)
cd %OLDD% cd %OLDD%
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/Release/usrsctp.lib $(<) cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/Release/usrsctp.lib $(<)

2
deps/libjuice vendored

View File

@ -23,17 +23,18 @@
#include "rtc/rtc.hpp" #include "rtc/rtc.hpp"
#include "parse_cl.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <algorithm> #include <algorithm>
#include <future>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <random> #include <random>
#include <thread>
#include <future>
#include <stdexcept> #include <stdexcept>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include "parse_cl.h"
using namespace rtc; using namespace rtc;
using namespace std; using namespace std;
@ -47,23 +48,21 @@ unordered_map<string, shared_ptr<PeerConnection>> peerConnectionMap;
unordered_map<string, shared_ptr<DataChannel>> dataChannelMap; unordered_map<string, shared_ptr<DataChannel>> dataChannelMap;
string localId; string localId;
bool echoDataChannelMessages = false;
shared_ptr<PeerConnection> createPeerConnection(const Configuration &config, shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
weak_ptr<WebSocket> wws, string id); weak_ptr<WebSocket> wws, string id);
void printReceived(bool echoed, string id, string type, size_t length);
string randomId(size_t length); string randomId(size_t length);
int main(int argc, char **argv) try { int main(int argc, char **argv) try {
auto params = std::make_unique<Cmdline>(argc, argv); auto params = std::make_unique<Cmdline>(argc, argv);
rtc::InitLogger(LogLevel::Debug); rtc::InitLogger(LogLevel::Info);
Configuration config; Configuration config;
string stunServer = ""; string stunServer = "";
if (params->noStun()) { if (params->noStun()) {
cout << "No STUN server is configured. Only local hosts and public IP addresses supported." << endl; cout << "No STUN server is configured. Only local hosts and public IP addresses supported."
<< endl;
} else { } else {
if (params->stunServer().substr(0, 5).compare("stun:") != 0) { if (params->stunServer().substr(0, 5).compare("stun:") != 0) {
stunServer = "stun:"; stunServer = "stun:";
@ -76,10 +75,6 @@ int main(int argc, char **argv) try {
localId = randomId(4); localId = randomId(4);
cout << "The local ID is: " << localId << endl; cout << "The local ID is: " << localId << endl;
echoDataChannelMessages = params->echoDataChannelMessages();
cout << "Received data channel messages will be "
<< (echoDataChannelMessages ? "echoed back to sender" : "printed to stdout") << endl;
auto ws = make_shared<WebSocket>(); auto ws = make_shared<WebSocket>();
std::promise<void> wsPromise; std::promise<void> wsPromise;
@ -171,20 +166,12 @@ int main(int argc, char **argv) try {
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; }); dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) { dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) {
static bool firstMessage = true; if (holds_alternative<string>(data))
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) { cout << "Message from " << id << " received: " << get<string>(data) << endl;
cout << "Message from " << id << " received: " << get<string>(message) << endl; else
firstMessage = false; cout << "Binary message from " << id
} else if (echoDataChannelMessages) { << " received, size=" << get<binary>(data).size() << endl;
bool echoed = false;
if (auto dc = wdc.lock()) {
dc->send(message);
echoed = true;
}
printReceived(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
get<string>(message).length());
}
}); });
dataChannelMap.emplace(id, dc); dataChannelMap.emplace(id, dc);
@ -237,20 +224,12 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; }); dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) { dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) {
static bool firstMessage = true; if (holds_alternative<string>(data))
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) { cout << "Message from " << id << " received: " << get<string>(data) << endl;
cout << "Message from " << id << " received: " << get<string>(message) << endl; else
firstMessage = false; cout << "Binary message from " << id
} else if (echoDataChannelMessages) { << " received, size=" << get<binary>(data).size() << endl;
bool echoed = false;
if (auto dc = wdc.lock()) {
dc->send(message);
echoed = true;
}
printReceived(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
get<string>(message).length());
}
}); });
dc->send("Hello from " + localId); dc->send("Hello from " + localId);
@ -262,20 +241,6 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
return pc; return pc;
}; };
// Helper function to print received pings
void printReceived(bool echoed, string id, string type, size_t length) {
static long count = 0;
static long freq = 100;
if (!(++count%freq)) {
cout << "Received " << count << " pings in total from " << id << ", most recent of type "
<< type << " and " << (echoed ? "" : "un") << "successfully echoed most recent ping of size "
<< length << " back to " << id << endl;
if (count >= (freq * 10) && freq < 1000000) {
freq *= 10;
}
}
}
// Helper function to generate a random ID // Helper function to generate a random ID
string randomId(size_t length) { string randomId(size_t length) {
static const string characters( static const string characters(

View File

@ -43,38 +43,30 @@ 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'}, {"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'},
{"webSocketPort", required_argument, NULL, 'x'}, {"webSocketPort", required_argument, NULL, 'x'},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
_program_name += argv[0]; _program_name += argv[0];
/* default values */ /* default values */
_e = false;
_n = 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;
_h = false; _h = false;
_v = false;
optind = 0; optind = 0;
while ((c = getopt_long (argc, argv, "s:t:w:x:enhv", 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': case 'n':
_n = true; _n = true;
break; break;
@ -124,11 +116,6 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
this->usage (EXIT_SUCCESS); this->usage (EXIT_SUCCESS);
break; break;
case 'v':
_v = true;
this->version (EXIT_SUCCESS);
break;
default: default:
this->usage (EXIT_FAILURE); this->usage (EXIT_FAILURE);
@ -155,8 +142,6 @@ void Cmdline::usage (int status)
std::cout << "\ std::cout << "\
usage: " << _program_name << " [ -enstwxhv ] \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\
Echo data channel messages back to sender rather than putting to stdout.\n\
[ -n ] [ --noStun ] (type=FLAG)\n\ [ -n ] [ --noStun ] (type=FLAG)\n\
Do NOT use a stun server (overrides -s and -t).\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\
@ -168,15 +153,8 @@ libdatachannel client implementing WebRTC Data Channels with WebSocket signaling
[ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\ [ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
Web socket server port.\n\ Web socket server port.\n\
[ -h ] [ --help ] (type=FLAG)\n\ [ -h ] [ --help ] (type=FLAG)\n\
Display this help and exit.\n\ Display this help and exit.\n";
[ -v ] [ --version ] (type=FLAG)\n\
Output version information and exit.\n";
} }
exit (status); exit (status);
} }
void Cmdline::version (int status)
{
std::cout << _program_name << " v0.5\n";
exit (status);
}

View File

@ -34,14 +34,12 @@ class Cmdline
{ {
private: private:
/* parameters */ /* parameters */
bool _e;
bool _n; bool _n;
std::string _s; std::string _s;
int _t; int _t;
std::string _w; std::string _w;
int _x; int _x;
bool _h; bool _h;
bool _v;
/* other stuff to keep track of */ /* other stuff to keep track of */
std::string _program_name; std::string _program_name;
@ -55,20 +53,15 @@ public:
/* usage function */ /* usage function */
void usage (int status); void usage (int status);
/* version function */
void version (int status);
/* 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; } 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 h () const { return _h; } bool h () const { return _h; }
bool v () const { return _v; }
}; };
#endif #endif

View File

@ -137,7 +137,7 @@ int main(int argc, char **argv) {
size_t candidate_size = 0; size_t candidate_size = 0;
if (getline(&candidate, &candidate_size, stdin)) { if (getline(&candidate, &candidate_size, stdin)) {
rtcAddRemoteCandidate(peer->pc, candidate, "0"); rtcAddRemoteCandidate(peer->pc, candidate, NULL);
free(candidate); free(candidate);
} else { } else {

View File

@ -140,7 +140,7 @@ int main(int argc, char **argv) {
char *candidate = NULL; char *candidate = NULL;
size_t candidate_size = 0; size_t candidate_size = 0;
if (getline(&candidate, &candidate_size, stdin)) { if (getline(&candidate, &candidate_size, stdin)) {
rtcAddRemoteCandidate(peer->pc, candidate, "0"); rtcAddRemoteCandidate(peer->pc, candidate, NULL);
free(candidate); free(candidate);
} else { } else {

View File

@ -59,6 +59,7 @@ function openSignaling(url) {
ws.onmessage = (e) => { ws.onmessage = (e) => {
if(typeof(e.data) != 'string') return; if(typeof(e.data) != 'string') return;
const message = JSON.parse(e.data); const message = JSON.parse(e.data);
console.log(message);
const { id, type } = message; const { id, type } = message;
let pc = peerConnectionMap[id]; let pc = peerConnectionMap[id];

View File

@ -35,6 +35,7 @@ public:
virtual void close() = 0; virtual void close() = 0;
virtual bool send(message_variant data) = 0; // returns false if buffered virtual bool send(message_variant data) = 0; // returns false if buffered
virtual bool send(const byte *data, size_t size) = 0;
virtual bool isOpen() const = 0; virtual bool isOpen() const = 0;
virtual bool isClosed() const = 0; virtual bool isClosed() const = 0;

View File

@ -50,7 +50,7 @@ public:
void close(void) override; void close(void) override;
bool send(message_variant data) override; bool send(message_variant data) override;
bool send(const byte *data, size_t size); bool send(const byte *data, size_t size) override;
template <typename Buffer> bool sendBuffer(const Buffer &buf); template <typename Buffer> bool sendBuffer(const Buffer &buf);
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last); template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);

View File

@ -32,6 +32,15 @@
namespace rtc { namespace rtc {
const string DEFAULT_AUDIO_PROFILE =
"minptime=10;maxaveragebitrate=96000;stereo=1;sprop-stereo=1;useinbandfec=1";
// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
const string DEFAULT_VIDEO_PROFILE =
"profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1";
class RTC_CPP_EXPORT Description { class RTC_CPP_EXPORT Description {
public: public:
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback }; enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
@ -131,9 +140,10 @@ public:
void removeFormat(const string &fmt); void removeFormat(const string &fmt);
void addSSRC(uint32_t ssrc, std::string name); void addSSRC(uint32_t ssrc, std::optional<string> name,
void addSSRC(uint32_t ssrc); std::optional<string> msid = nullopt);
void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, string name); void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
std::optional<string> msid = nullopt);
bool hasSSRC(uint32_t ssrc); bool hasSSRC(uint32_t ssrc);
std::vector<uint32_t> getSSRCs(); std::vector<uint32_t> getSSRCs();
@ -142,6 +152,9 @@ public:
bool hasPayloadType(int payloadType) const; bool hasPayloadType(int payloadType) const;
void addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
unsigned int clockRate);
virtual void parseSdpLine(string_view line) override; virtual void parseSdpLine(string_view line) override;
struct RTPMap { struct RTPMap {
@ -150,7 +163,7 @@ public:
void removeFB(const string &string); void removeFB(const string &string);
void addFB(const string &string); void addFB(const string &string);
void addAttribute(std::string attr) { fmtps.emplace_back(attr); } void addAttribute(string attr) { fmtps.emplace_back(std::move(attr)); }
int pt; int pt;
string format; string format;
@ -181,13 +194,17 @@ public:
public: public:
void addRTPMap(const RTPMap &map); void addRTPMap(const RTPMap &map);
void removeSSRC(uint32_t oldSSRC);
}; };
class RTC_CPP_EXPORT Audio : public Media { class RTC_CPP_EXPORT Audio : public Media {
public: public:
Audio(string mid = "audio", Direction dir = Direction::SendOnly); Audio(string mid = "audio", Direction dir = Direction::SendOnly);
void addAudioCodec(int payloadType, const string &codec); void addAudioCodec(int payloadType, string codec,
std::optional<string> profile = DEFAULT_AUDIO_PROFILE);
void addOpusCodec(int payloadType); void addOpusCodec(int payloadType);
}; };
@ -195,7 +212,9 @@ public:
public: public:
Video(string mid = "video", Direction dir = Direction::SendOnly); Video(string mid = "video", Direction dir = Direction::SendOnly);
void addVideoCodec(int payloadType, const string &codec); void addVideoCodec(int payloadType, string codec,
std::optional<string> profile = DEFAULT_VIDEO_PROFILE);
void addH264Codec(int payloadType); void addH264Codec(int payloadType);
void addVP8Codec(int payloadType); void addVP8Codec(int payloadType);
void addVP9Codec(int payloadType); void addVP9Codec(int payloadType);

View File

@ -157,6 +157,8 @@ private:
void processLocalCandidate(Candidate candidate); void processLocalCandidate(Candidate candidate);
void processRemoteDescription(Description description); void processRemoteDescription(Description description);
void processRemoteCandidate(Candidate candidate); void processRemoteCandidate(Candidate candidate);
string localBundleMid() const;
void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel); void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
void triggerTrack(std::shared_ptr<Track> track); void triggerTrack(std::shared_ptr<Track> track);
bool changeState(State state); bool changeState(State state);

View File

@ -66,10 +66,17 @@ public:
inline uint32_t ssrc() const { return ntohl(_ssrc); } inline uint32_t ssrc() const { return ntohl(_ssrc); }
inline size_t getSize() const { inline size_t getSize() const {
return ((char *)&csrc) - ((char *)this) + sizeof(SSRC) * csrcCount(); return reinterpret_cast<const char *>(&csrc) - reinterpret_cast<const char *>(this) +
sizeof(SSRC) * csrcCount();
} }
char *getBody() const { return ((char *)&csrc) + sizeof(SSRC) * csrcCount(); } [[nodiscard]] char *getBody() {
return reinterpret_cast<char *>(&csrc) + sizeof(SSRC) * csrcCount();
}
[[nodiscard]] const char *getBody() const {
return reinterpret_cast<const char *>(&csrc) + sizeof(SSRC) * csrcCount();
}
inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); } inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
inline void setPayloadType(uint8_t newPayloadType) { inline void setPayloadType(uint8_t newPayloadType) {
@ -117,18 +124,19 @@ public:
} }
inline void setSSRC(SSRC in_ssrc) { this->ssrc = htonl(in_ssrc); } inline void setSSRC(SSRC in_ssrc) { this->ssrc = htonl(in_ssrc); }
inline SSRC getSSRC() const { return ntohl(ssrc); } [[nodiscard]] inline SSRC getSSRC() const { return ntohl(ssrc); }
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost, inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets) { [[maybe_unused]] unsigned int totalPackets) {
// TODO Implement loss percentages. // TODO Implement loss percentages.
_fractionLostAndPacketsLost = 0; _fractionLostAndPacketsLost = 0;
} }
inline unsigned int getLossPercentage() const {
[[nodiscard]] inline unsigned int getLossPercentage() const {
// TODO Implement loss percentages. // TODO Implement loss percentages.
return 0; return 0;
} }
inline unsigned int getPacketLostCount() const { [[nodiscard]] inline unsigned int getPacketLostCount() const {
// TODO Implement total packets lost. // TODO Implement total packets lost.
return 0; return 0;
} }
@ -145,13 +153,13 @@ public:
inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); } inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); } inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; } [[nodiscard]] inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
inline void setDelaySinceSR(uint32_t sr) { inline void setDelaySinceSR(uint32_t sr) {
// The delay, expressed in units of 1/65536 seconds // The delay, expressed in units of 1/65536 seconds
_delaySinceLastReport = htonl(sr); _delaySinceLastReport = htonl(sr);
} }
inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); } [[nodiscard]] inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
inline void log() const { inline void log() const {
PLOG_VERBOSE << "RTCP report block: " PLOG_VERBOSE << "RTCP report block: "
@ -242,8 +250,10 @@ public:
this->_senderSSRC = htonl(senderSSRC); this->_senderSSRC = htonl(senderSSRC);
} }
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; } [[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; } [[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const {
return &_reportBlocks + num;
}
[[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.
@ -280,8 +290,10 @@ private:
RTCP_ReportBlock _reportBlocks; RTCP_ReportBlock _reportBlocks;
public: public:
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; } [[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; } [[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const {
return &_reportBlocks + num;
}
inline SSRC senderSSRC() const { return ntohl(_senderSSRC); } inline SSRC senderSSRC() const { return ntohl(_senderSSRC); }
inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); } inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); }
@ -477,11 +489,15 @@ public:
return ntohs(*(uint16_t *)(header.getBody())); return ntohs(*(uint16_t *)(header.getBody()));
} }
char *getBody() { return header.getBody() + sizeof(uint16_t); } [[nodiscard]] char *getBody() { return header.getBody() + sizeof(uint16_t); }
size_t getBodySize(size_t totalSize) { return totalSize - ((char *)getBody() - (char *)this); } [[nodiscard]] const char *getBody() const { return header.getBody() + sizeof(uint16_t); }
RTP &getHeader() { return header; } [[nodiscard]] size_t getBodySize(size_t totalSize) {
return totalSize - (getBody() - reinterpret_cast<char *>(this));
}
[[nodiscard]] RTP &getHeader() { return header; }
size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) { size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
header.setSeqNumber(getOriginalSeqNo()); header.setSeqNumber(getOriginalSeqNo());

View File

@ -47,7 +47,7 @@ public:
void close(void) override; void close(void) override;
bool send(message_variant data) override; bool send(message_variant data) override;
bool send(const byte *data, size_t size); bool send(const byte *data, size_t size) override;
bool isOpen(void) const override; bool isOpen(void) const override;
bool isClosed(void) const override; bool isClosed(void) const override;

View File

@ -49,6 +49,7 @@ public:
struct Configuration { struct Configuration {
bool disableTlsVerification = false; // if true, don't verify the TLS certificate bool disableTlsVerification = false; // if true, don't verify the TLS certificate
std::vector<string> protocols;
}; };
WebSocket(std::optional<Configuration> config = nullopt); WebSocket(std::optional<Configuration> config = nullopt);
@ -59,6 +60,7 @@ public:
void open(const string &url); void open(const string &url);
void close() override; void close() override;
bool send(const message_variant data) override; bool send(const message_variant data) override;
bool send(const byte *data, size_t size) override;
bool isOpen() const override; bool isOpen() const override;
bool isClosed() const override; bool isClosed() const override;

View File

@ -117,9 +117,9 @@ void Candidate::parse(string candidate) {
mTransportType = TransportType::Udp; mTransportType = TransportType::Udp;
} else if (transport == "TCP" || transport == "tcp") { } else if (transport == "TCP" || transport == "tcp") {
// Peek tail to find TCP type // Peek tail to find TCP type
std::istringstream iss(mTail); std::istringstream tiss(mTail);
string tcptype_, tcptype; string tcptype_, tcptype;
if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") { if (tiss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end()) if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
mTransportType = it->second; mTransportType = it->second;
else else

View File

@ -29,13 +29,13 @@
#include <unordered_map> #include <unordered_map>
using std::shared_ptr; using std::shared_ptr;
using std::size_t;
using std::string;
using std::string_view;
using std::chrono::system_clock; using std::chrono::system_clock;
namespace { namespace {
using std::string;
using std::string_view;
inline bool match_prefix(string_view str, string_view prefix) { inline bool match_prefix(string_view str, string_view prefix) {
return str.size() >= prefix.size() && return str.size() >= prefix.size() &&
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end(); std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
@ -483,8 +483,7 @@ string Description::Entry::generateSdpLines(string_view eol) const {
} }
for (const auto &attr : mAttributes) { for (const auto &attr : mAttributes) {
if (attr.find("extmap") == std::string::npos && if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
attr.find("rtcp-rsize") == std::string::npos)
sdp << "a=" << attr << eol; sdp << "a=" << attr << eol;
} }
@ -519,12 +518,21 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
return mAttributes.erase(it); return mAttributes.erase(it);
} }
void Description::Media::addSSRC(uint32_t ssrc, std::string name) { void Description::Media::addSSRC(uint32_t ssrc, std::optional<string> name,
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name); std::optional<string> msid) {
if (name)
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
else
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
if (msid)
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + *msid);
mSsrcs.emplace_back(ssrc); mSsrcs.emplace_back(ssrc);
} }
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::string name) { void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
std::optional<string> msid) {
auto it = mAttributes.begin(); auto it = mAttributes.begin();
while (it != mAttributes.end()) { while (it != mAttributes.end()) {
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) { if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
@ -532,11 +540,17 @@ void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::strin
} else } else
it++; it++;
} }
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name); addSSRC(ssrc, std::move(name), std::move(msid));
} }
void Description::Media::addSSRC(uint32_t ssrc) { void Description::Media::removeSSRC(uint32_t oldSSRC) {
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc)); auto it = mAttributes.begin();
while (it != mAttributes.end()) {
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
it = mAttributes.erase(it);
} else
it++;
}
} }
bool Description::Media::hasSSRC(uint32_t ssrc) { bool Description::Media::hasSSRC(uint32_t ssrc) {
@ -689,32 +703,17 @@ void Description::Media::removeFormat(const string &fmt) {
} }
} }
void Description::Video::addVideoCodec(int payloadType, const string &codec) { void Description::Video::addVideoCodec(int payloadType, string codec,
std::optional<string> profile) {
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000"); RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
map.addFB("nack"); map.addFB("nack");
map.addFB("nack pli"); map.addFB("nack pli");
// map.addFB("nack fir"); // map.addFB("ccm fir");
map.addFB("goog-remb"); map.addFB("goog-remb");
if (codec == "H264") { if (profile)
// Use Constrained Baseline profile Level 4.2 (necessary for Firefox) map.fmtps.emplace_back(*profile);
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
map.fmtps.emplace_back(
"profile-level-id=4de01f;packetization-mode=1;level-asymmetry-allowed=1");
// Because certain Android devices don't like me, let us just negotiate some random
{
RTPMap map(std::to_string(payloadType + 1) + ' ' + codec + "/90000");
map.addFB("nack");
map.addFB("nack pli");
// map.addFB("nack fir");
map.addFB("goog-remb");
addRTPMap(map);
}
}
addRTPMap(map); addRTPMap(map);
// // RTX Packets
/* TODO /* TODO
* TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't * TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
* negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we * negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
@ -722,24 +721,34 @@ void Description::Video::addVideoCodec(int payloadType, const string &codec) {
* rebuild the original packet before we send it distribute it to each track. Three) we complain * rebuild the original packet before we send it distribute it to each track. Three) we complain
* to mozilla. This one probably won't do much. * to mozilla. This one probably won't do much.
*/ */
// RTX Packets
// RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000"); // RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000");
// // TODO rtx-time is how long can a request be stashed for before needing to resend it. // // TODO rtx-time is how long can a request be stashed for before needing to resend it.
// Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) + // Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
// ";rtx-time=3000"); addRTPMap(rtx); // ";rtx-time=3000"); addRTPMap(rtx);
} }
void Description::Audio::addAudioCodec(int payloadType, const string &codec) { void Description::Audio::addAudioCodec(int payloadType, string codec,
std::optional<string> profile) {
// TODO This 48000/2 should be parameterized // TODO This 48000/2 should be parameterized
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2"); RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
map.fmtps.emplace_back("maxaveragebitrate=96000; stereo=1; sprop-stereo=1; useinbandfec=1"); if (profile)
map.fmtps.emplace_back(*profile);
addRTPMap(map);
}
void Description::Media::addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
unsigned int clockRate) {
RTPMap map(std::to_string(payloadType) + " RTX/" + std::to_string(clockRate));
map.fmtps.emplace_back("apt=" + std::to_string(originalPayloadType));
addRTPMap(map); addRTPMap(map);
} }
void Description::Video::addH264Codec(int pt) { addVideoCodec(pt, "H264"); } void Description::Video::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
void Description::Video::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8"); } void Description::Video::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8", nullopt); }
void Description::Video::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9"); } void Description::Video::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9", nullopt); }
void Description::Media::setBitrate(int bitrate) { mBas = bitrate; } void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
@ -808,7 +817,7 @@ void Description::Media::parseSdpLine(string_view line) {
} else if (key == "rtcp-mux") { } else if (key == "rtcp-mux") {
// always added // always added
} else if (key == "ssrc") { } else if (key == "ssrc") {
mSsrcs.emplace_back(std::stoul((std::string)value)); mSsrcs.emplace_back(std::stoul(string(value)));
} else { } else {
Entry::parseSdpLine(line); Entry::parseSdpLine(line);
} }
@ -828,7 +837,7 @@ std::vector<uint32_t> Description::Media::getSSRCs() {
for (auto &val : mAttributes) { for (auto &val : mAttributes) {
PLOG_DEBUG << val; PLOG_DEBUG << val;
if (val.find("ssrc:") == 0) { if (val.find("ssrc:") == 0) {
vec.emplace_back(std::stoul((std::string)val.substr(5, val.find(" ")))); vec.emplace_back(std::stoul(string(val.substr(5, val.find(" ")))));
} }
} }
return vec; return vec;
@ -852,7 +861,7 @@ Description::Media::RTPMap::RTPMap(string_view mline) { setMLine(mline); }
void Description::Media::RTPMap::removeFB(const string &str) { void Description::Media::RTPMap::removeFB(const string &str) {
auto it = rtcpFbs.begin(); auto it = rtcpFbs.begin();
while (it != rtcpFbs.end()) { while (it != rtcpFbs.end()) {
if (it->find(str) != std::string::npos) { if (it->find(str) != string::npos) {
it = rtcpFbs.erase(it); it = rtcpFbs.erase(it);
} else } else
it++; it++;

View File

@ -110,7 +110,7 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
if (err == srtp_err_status_replay_fail) if (err == srtp_err_status_replay_fail)
throw std::runtime_error("SRTCP packet is a replay"); throw std::runtime_error("SRTCP packet is a replay");
else if (err == srtp_err_status_no_ctx) { else if (err == srtp_err_status_no_ctx) {
auto ssrc = ((RTCP_SR *)message->data())->senderSSRC(); auto ssrc = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
PLOG_INFO << "Adding SSRC to SRTCP: " << ssrc; PLOG_INFO << "Adding SSRC to SRTCP: " << ssrc;
addSSRC(ssrc); addSSRC(ssrc);
if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size))) if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
@ -127,11 +127,11 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
if (err == srtp_err_status_replay_fail) if (err == srtp_err_status_replay_fail)
throw std::runtime_error("Outgoing SRTP packet is a replay"); throw std::runtime_error("Outgoing SRTP packet is a replay");
else if (err == srtp_err_status_no_ctx) { else if (err == srtp_err_status_no_ctx) {
auto ssrc = ((RTP *)message->data())->ssrc(); auto ssrc = reinterpret_cast<RTP *>(message->data())->ssrc();
PLOG_INFO << "Adding SSRC to RTP: " << ssrc; PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
addSSRC(ssrc); addSSRC(ssrc);
if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size))) if ((err = srtp_protect(mSrtpOut, message->data(), &size)))
throw std::runtime_error("SRTCP protect error, status=" + throw std::runtime_error("SRTP protect error, status=" +
to_string(static_cast<int>(err))); to_string(static_cast<int>(err)));
} else } else
throw std::runtime_error("SRTP protect error, status=" + throw std::runtime_error("SRTP protect error, status=" +
@ -196,7 +196,7 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
else if (err == srtp_err_status_auth_fail) else if (err == srtp_err_status_auth_fail)
PLOG_WARNING << "Incoming SRTCP packet failed authentication check"; PLOG_WARNING << "Incoming SRTCP packet failed authentication check";
else if (err == srtp_err_status_no_ctx) { else if (err == srtp_err_status_no_ctx) {
auto ssrc = ((RTCP_SR *)message->data())->senderSSRC(); auto ssrc = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
PLOG_INFO << "Adding SSRC to RTCP: " << ssrc; PLOG_INFO << "Adding SSRC to RTCP: " << ssrc;
addSSRC(ssrc); addSSRC(ssrc);
if ((err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size))) if ((err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)))
@ -210,8 +210,7 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
} }
PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size; PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
message->type = Message::Type::Control; message->type = Message::Type::Control;
auto rtp = (RTCP_SR *)message->data(); message->stream = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
message->stream = rtp->senderSSRC();
} else { } else {
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size; PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) { if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
@ -220,21 +219,20 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
else if (err == srtp_err_status_auth_fail) else if (err == srtp_err_status_auth_fail)
PLOG_WARNING << "Incoming SRTP packet failed authentication check"; PLOG_WARNING << "Incoming SRTP packet failed authentication check";
else if (err == srtp_err_status_no_ctx) { else if (err == srtp_err_status_no_ctx) {
auto ssrc = ((RTP *)message->data())->ssrc(); auto ssrc = reinterpret_cast<RTP *>(message->data())->ssrc();
PLOG_INFO << "Adding SSRC to RTP: " << ssrc; PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
addSSRC(ssrc); addSSRC(ssrc);
if ((err = srtp_unprotect(mSrtpIn, message->data(), &size))) if ((err = srtp_unprotect(mSrtpIn, message->data(), &size)))
throw std::runtime_error("SRTCP unprotect error, status=" + throw std::runtime_error("SRTP unprotect error, status=" +
to_string(static_cast<int>(err))); to_string(static_cast<int>(err)));
} else } else
PLOG_WARNING << "SRTP unprotect error, status=" << err PLOG_WARNING << "SRTP unprotect error, status=" << err
<< " SSRC=" << ((RTP *)message->data())->ssrc(); << " SSRC=" << reinterpret_cast<RTP *>(message->data())->ssrc();
return; return;
} }
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size; PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
message->type = Message::Type::Binary; message->type = Message::Type::Binary;
auto rtp = (RTP *)message->data(); message->stream = reinterpret_cast<RTP *>(message->data())->ssrc();
message->stream = rtp->ssrc();
} }
message->resize(size); message->resize(size);

View File

@ -168,7 +168,9 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0; return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0;
} }
void IceTransport::gatherLocalCandidates() { void IceTransport::gatherLocalCandidates(string mid) {
mMid = std::move(mid);
// Change state now as candidates calls can be synchronous // Change state now as candidates calls can be synchronous
changeGatheringState(GatheringState::InProgress); changeGatheringState(GatheringState::InProgress);
@ -582,7 +584,9 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
return ret > 0; return ret > 0;
} }
void IceTransport::gatherLocalCandidates() { void IceTransport::gatherLocalCandidates(string mid) {
mMid = std::move(mid);
// Change state now as candidates calls can be synchronous // Change state now as candidates calls can be synchronous
changeGatheringState(GatheringState::InProgress); changeGatheringState(GatheringState::InProgress);

View File

@ -56,7 +56,7 @@ public:
Description getLocalDescription(Description::Type type) const; Description getLocalDescription(Description::Type type) const;
void setRemoteDescription(const Description &description); void setRemoteDescription(const Description &description);
bool addRemoteCandidate(const Candidate &candidate); bool addRemoteCandidate(const Candidate &candidate);
void gatherLocalCandidates(); void gatherLocalCandidates(string mid);
std::optional<string> getLocalAddress() const; std::optional<string> getLocalAddress() const;
std::optional<string> getRemoteAddress() const; std::optional<string> getRemoteAddress() const;

View File

@ -189,13 +189,14 @@ void PeerConnection::setLocalDescription(Description::Type type) {
auto iceTransport = initIceTransport(); auto iceTransport = initIceTransport();
Description localDescription = iceTransport->getLocalDescription(type); Description local = iceTransport->getLocalDescription(type);
processLocalDescription(std::move(localDescription)); processLocalDescription(std::move(local));
changeSignalingState(newSignalingState); changeSignalingState(newSignalingState);
if (mGatheringState == GatheringState::New) if (mGatheringState == GatheringState::New) {
iceTransport->gatherLocalCandidates(); iceTransport->gatherLocalCandidates(localBundleMid());
}
} }
void PeerConnection::setRemoteDescription(Description description) { void PeerConnection::setRemoteDescription(Description description) {
@ -648,10 +649,10 @@ void PeerConnection::forwardMessage(message_ptr message) {
stream % 2 == remoteParity) { stream % 2 == remoteParity) {
channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport, channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport,
message->stream); stream);
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this, channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
weak_ptr<DataChannel>{channel})); weak_ptr<DataChannel>{channel}));
mDataChannels.emplace(message->stream, channel); mDataChannels.emplace(stream, channel);
} else { } else {
// Invalid, close the DataChannel // Invalid, close the DataChannel
sctpTransport->closeStream(message->stream); sctpTransport->closeStream(message->stream);
@ -1013,7 +1014,10 @@ void PeerConnection::processLocalDescription(Description description) {
if (!description.hasApplication()) { if (!description.hasApplication()) {
std::shared_lock lock(mDataChannelsMutex); std::shared_lock lock(mDataChannelsMutex);
if (!mDataChannels.empty()) { if (!mDataChannels.empty()) {
Description::Application app("data"); unsigned int m = 0;
while (description.hasMid(std::to_string(m)))
++m;
Description::Application app(std::to_string(m));
app.setSctpPort(DEFAULT_SCTP_PORT); app.setSctpPort(DEFAULT_SCTP_PORT);
app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE); app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
@ -1133,7 +1137,7 @@ void PeerConnection::processRemoteCandidate(Candidate candidate) {
} else { } else {
// We might need a lookup, do it asynchronously // We might need a lookup, do it asynchronously
// We don't use the thread pool because we have no control on the timeout // We don't use the thread pool because we have no control on the timeout
if (auto iceTransport = std::atomic_load(&mIceTransport)) { if ((iceTransport = std::atomic_load(&mIceTransport))) {
weak_ptr<IceTransport> weakIceTransport{iceTransport}; weak_ptr<IceTransport> weakIceTransport{iceTransport};
std::thread t([weakIceTransport, candidate = std::move(candidate)]() mutable { std::thread t([weakIceTransport, candidate = std::move(candidate)]() mutable {
if (candidate.resolve(Candidate::ResolveMode::Lookup)) if (candidate.resolve(Candidate::ResolveMode::Lookup))
@ -1145,6 +1149,11 @@ void PeerConnection::processRemoteCandidate(Candidate candidate) {
} }
} }
string PeerConnection::localBundleMid() const {
std::lock_guard lock(mLocalDescriptionMutex);
return mLocalDescription ? mLocalDescription->bundleMid() : "0";
}
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) { void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
auto dataChannel = weakDataChannel.lock(); auto dataChannel = weakDataChannel.lock();
if (!dataChannel) if (!dataChannel)

View File

@ -126,7 +126,7 @@ bool RtcpReceivingSession::requestKeyframe() {
void RtcpReceivingSession::pushPLI() { void RtcpReceivingSession::pushPLI() {
auto msg = rtc::make_message(rtc::RTCP_PLI::size(), rtc::Message::Type::Control); auto msg = rtc::make_message(rtc::RTCP_PLI::size(), rtc::Message::Type::Control);
auto *pli = (rtc::RTCP_PLI *)msg->data(); auto *pli = reinterpret_cast<rtc::RTCP_PLI *>(msg->data());
pli->preparePacket(mSsrc); pli->preparePacket(mSsrc);
send(msg); send(msg);
} }

View File

@ -18,15 +18,26 @@
#include "threadpool.hpp" #include "threadpool.hpp"
#include <cstdlib>
namespace {
void joinThreadPoolInstance() {
rtc::ThreadPool::Instance().join();
}
}
namespace rtc { namespace rtc {
ThreadPool &ThreadPool::Instance() { ThreadPool &ThreadPool::Instance() {
// Init handles joining on cleanup
static ThreadPool *instance = new ThreadPool; static ThreadPool *instance = new ThreadPool;
return *instance; return *instance;
} }
ThreadPool::~ThreadPool() { join(); } ThreadPool::ThreadPool() {
std::atexit(joinThreadPoolInstance);
}
ThreadPool::~ThreadPool() {}
int ThreadPool::count() const { int ThreadPool::count() const {
std::unique_lock lock(mWorkersMutex); std::unique_lock lock(mWorkersMutex);

View File

@ -56,7 +56,7 @@ public:
auto enqueue(F &&f, Args &&... args) -> invoke_future_t<F, Args...>; auto enqueue(F &&f, Args &&... args) -> invoke_future_t<F, Args...>;
protected: protected:
ThreadPool() = default; ThreadPool();
~ThreadPool(); ~ThreadPool();
std::function<void()> dequeue(); // returns null function if joining std::function<void()> dequeue(); // returns null function if joining

View File

@ -118,6 +118,10 @@ void WebSocket::remoteClose() {
bool WebSocket::send(message_variant data) { return outgoing(make_message(std::move(data))); } bool WebSocket::send(message_variant data) { return outgoing(make_message(std::move(data))); }
bool WebSocket::send(const byte *data, size_t size) {
return outgoing(make_message(data, data + size));
}
bool WebSocket::isOpen() const { return mState == State::Open; } bool WebSocket::isOpen() const { return mState == State::Open; }
bool WebSocket::isClosed() const { return mState == State::Closed; } bool WebSocket::isClosed() const { return mState == State::Closed; }
@ -287,8 +291,14 @@ shared_ptr<WsTransport> WebSocket::initWsTransport() {
shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport); shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport);
if (!lower) if (!lower)
lower = std::atomic_load(&mTcpTransport); lower = std::atomic_load(&mTcpTransport);
WsTransport::Configuration wsConfig = {};
wsConfig.host = mHost;
wsConfig.path = mPath;
wsConfig.protocols = mConfig.protocols;
auto transport = std::make_shared<WsTransport>( auto transport = std::make_shared<WsTransport>(
lower, mHost, mPath, weak_bind(&WebSocket::incoming, this, _1), lower, wsConfig, weak_bind(&WebSocket::incoming, this, _1),
[this, weak_this = weak_from_this()](State state) { [this, weak_this = weak_from_this()](State state) {
auto shared_this = weak_this.lock(); auto shared_this = weak_this.lock();
if (!shared_this) if (!shared_this)

View File

@ -24,8 +24,10 @@
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
#include <chrono> #include <chrono>
#include <iterator>
#include <list> #include <list>
#include <map> #include <map>
#include <numeric>
#include <random> #include <random>
#include <regex> #include <regex>
@ -52,17 +54,17 @@ using std::to_string;
using random_bytes_engine = using random_bytes_engine =
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned short>; std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned short>;
WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string path, WsTransport::WsTransport(std::shared_ptr<Transport> lower, Configuration config,
message_callback recvCallback, state_callback stateCallback) message_callback recvCallback, state_callback stateCallback)
: Transport(lower, std::move(stateCallback)), mHost(std::move(host)), mPath(std::move(path)) { : Transport(lower, std::move(stateCallback)), mConfig(std::move(config)) {
onRecv(recvCallback); onRecv(recvCallback);
PLOG_DEBUG << "Initializing WebSocket transport"; PLOG_DEBUG << "Initializing WebSocket transport";
if (mHost.empty()) if (mConfig.host.empty())
throw std::invalid_argument("WebSocket HTTP host cannot be empty"); throw std::invalid_argument("WebSocket HTTP host cannot be empty");
if (mPath.empty()) if (mConfig.path.empty())
throw std::invalid_argument("WebSocket HTTP path cannot be empty"); throw std::invalid_argument("WebSocket HTTP path cannot be empty");
} }
@ -153,7 +155,7 @@ void WsTransport::close() {
} }
bool WsTransport::sendHttpRequest() { bool WsTransport::sendHttpRequest() {
PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mPath; PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mConfig.path;
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());
@ -163,18 +165,27 @@ bool WsTransport::sendHttpRequest() {
auto k = reinterpret_cast<uint8_t *>(key.data()); auto k = reinterpret_cast<uint8_t *>(key.data());
std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); }); std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); });
const string request = "GET " + mPath + string appendHeader = "";
if (mConfig.protocols.size() > 0) {
appendHeader +=
"Sec-WebSocket-Protocol: " +
std::accumulate(mConfig.protocols.begin(), mConfig.protocols.end(), string(),
[](const string &a, const string &b) -> string {
return a + (a.length() > 0 ? "," : "") + b;
}) +
"\r\n";
}
const string request = "GET " + mConfig.path +
" HTTP/1.1\r\n" " HTTP/1.1\r\n"
"Host: " + "Host: " +
mHost + mConfig.host +
"\r\n" "\r\n"
"Connection: Upgrade\r\n" "Connection: Upgrade\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: " + "Sec-WebSocket-Key: " +
to_base64(key) + to_base64(key) + "\r\n" + std::move(appendHeader) + "\r\n";
"\r\n"
"\r\n";
auto data = reinterpret_cast<const byte *>(request.data()); auto data = reinterpret_cast<const byte *>(request.data());
auto size = request.size(); auto size = request.size();
@ -227,8 +238,14 @@ size_t WsTransport::readHttpResponse(const byte *buffer, size_t size) {
} }
auto h = headers.find("upgrade"); auto h = headers.find("upgrade");
if (h == headers.end() || h->second != "websocket") if (h == headers.end())
throw std::runtime_error("WebSocket update header missing or mismatching"); throw std::runtime_error("WebSocket update header missing");
string upgrade;
std::transform(h->second.begin(), h->second.end(), std::back_inserter(upgrade),
[](char c) { return std::tolower(c); });
if (upgrade != "websocket")
throw std::runtime_error("WebSocket update header mismatching: " + h->second);
h = headers.find("sec-websocket-accept"); h = headers.find("sec-websocket-accept");
if (h == headers.end()) if (h == headers.end())

View File

@ -31,7 +31,13 @@ class TlsTransport;
class WsTransport : public Transport { class WsTransport : public Transport {
public: public:
WsTransport(std::shared_ptr<Transport> lower, string host, string path, struct Configuration {
string host;
string path = "/";
std::vector<string> protocols;
};
WsTransport(std::shared_ptr<Transport> lower, Configuration config,
message_callback recvCallback, state_callback stateCallback); message_callback recvCallback, state_callback stateCallback);
~WsTransport(); ~WsTransport();
@ -68,12 +74,12 @@ private:
void recvFrame(const Frame &frame); void recvFrame(const Frame &frame);
bool sendFrame(const Frame &frame); bool sendFrame(const Frame &frame);
const string mHost; const Configuration mConfig;
const string mPath;
binary mBuffer; binary mBuffer;
binary mPartial; binary mPartial;
Opcode mPartialOpcode; Opcode mPartialOpcode;
}; };
} // namespace rtc } // namespace rtc

View File

@ -227,12 +227,6 @@ int test_capi_connectivity_main() {
goto error; goto error;
} }
if (peer1->signalingState != RTC_SIGNALING_STABLE ||
peer2->signalingState != RTC_SIGNALING_STABLE) {
fprintf(stderr, "Signaling state is not stable\n");
goto error;
}
if (!peer1->connected || !peer2->connected) { if (!peer1->connected || !peer2->connected) {
fprintf(stderr, "DataChannel is not connected\n"); fprintf(stderr, "DataChannel is not connected\n");
goto error; goto error;