Compare commits

..

11 Commits

11 changed files with 274 additions and 279 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.7) cmake_minimum_required (VERSION 3.7)
project (libdatachannel project (libdatachannel
DESCRIPTION "WebRTC DataChannels Library" DESCRIPTION "WebRTC DataChannels Library"
VERSION 0.4.3 VERSION 0.4.5
LANGUAGES CXX) LANGUAGES CXX)
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF) option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
@ -156,6 +156,6 @@ add_executable(datachannel-answerer ${TESTS_ANSWERER_SOURCES})
set_target_properties(datachannel-answerer PROPERTIES set_target_properties(datachannel-answerer PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
CXX_STANDARD 17) CXX_STANDARD 17)
set_target_properties(datachannel-answerer PROPERTIES OUTPUT_NAME datachannel) set_target_properties(datachannel-answerer PROPERTIES OUTPUT_NAME answerer)
target_link_libraries(datachannel-answerer datachannel) target_link_libraries(datachannel-answerer datachannel)

2
deps/libjuice vendored

View File

@ -35,6 +35,7 @@ public:
enum class Role { ActPass = 0, Passive = 1, Active = 2 }; enum class Role { ActPass = 0, Passive = 1, Active = 2 };
Description(const string &sdp, const string &typeString = ""); Description(const string &sdp, const string &typeString = "");
Description(const string &sdp, Type type);
Description(const string &sdp, Type type, Role role); Description(const string &sdp, Type type, Role role);
Type type() const; Type type() const;
@ -47,6 +48,7 @@ public:
std::optional<size_t> maxMessageSize() const; std::optional<size_t> maxMessageSize() const;
bool trickleEnabled() const; bool trickleEnabled() const;
void hintType(Type type);
void setFingerprint(string fingerprint); void setFingerprint(string fingerprint);
void setSctpPort(uint16_t port); void setSctpPort(uint16_t port);
void setMaxMessageSize(size_t size); void setMaxMessageSize(size_t size);

View File

@ -45,12 +45,13 @@ inline void trim_end(string &str) {
namespace rtc { namespace rtc {
Description::Description(const string &sdp, const string &typeString) Description::Description(const string &sdp, const string &typeString)
: Description(sdp, stringToType(typeString), Description::Role::ActPass) {} : Description(sdp, stringToType(typeString)) {}
Description::Description(const string &sdp, Type type) : Description(sdp, type, Role::ActPass) {}
Description::Description(const string &sdp, Type type, Role role) Description::Description(const string &sdp, Type type, Role role)
: mType(type), mRole(role), mMid("0"), mIceUfrag("0"), mIcePwd("0"), mTrickle(true) { : mType(Type::Unspec), mRole(role), mMid("0"), mIceUfrag(""), mIcePwd(""), mTrickle(true) {
if (mType == Type::Answer && mRole == Role::ActPass) hintType(type);
mRole = Role::Passive; // ActPass is illegal for an answer, so default to passive
auto seed = std::chrono::system_clock::now().time_since_epoch().count(); auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed); std::default_random_engine generator(seed);
@ -109,6 +110,14 @@ std::optional<size_t> Description::maxMessageSize() const { return mMaxMessageSi
bool Description::trickleEnabled() const { return mTrickle; } bool Description::trickleEnabled() const { return mTrickle; }
void Description::hintType(Type type) {
if (mType == Type::Unspec) {
mType = type;
if (mType == Type::Answer && mRole == Role::ActPass)
mRole = Role::Passive; // ActPass is illegal for an answer, so default to passive
}
}
void Description::setFingerprint(string fingerprint) { void Description::setFingerprint(string fingerprint) {
mFingerprint.emplace(std::move(fingerprint)); mFingerprint.emplace(std::move(fingerprint));
} }

View File

@ -73,7 +73,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
// RFC 8261: SCTP performs segmentation and reassembly based on the path MTU. // RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
// Therefore, the DTLS layer MUST NOT use any compression algorithm. // Therefore, the DTLS layer MUST NOT use any compression algorithm.
// See https://tools.ietf.org/html/rfc8261#section-5 // See https://tools.ietf.org/html/rfc8261#section-5
const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL"; const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
const char *err_pos = NULL; const char *err_pos = NULL;
check_gnutls(gnutls_priority_set_direct(mSession, priorities, &err_pos), check_gnutls(gnutls_priority_set_direct(mSession, priorities, &err_pos),
"Unable to set TLS priorities"); "Unable to set TLS priorities");

View File

@ -114,9 +114,6 @@ void IceTransport::setRemoteDescription(const Description &description) {
mRole = description.role() == Description::Role::Active ? Description::Role::Passive mRole = description.role() == Description::Role::Active ? Description::Role::Passive
: Description::Role::Active; : Description::Role::Active;
mMid = description.mid(); mMid = description.mid();
// TODO
// mTrickleTimeout = description.trickleEnabled() ? 30s : 0s;
if (juice_set_remote_description(mAgent.get(), string(description).c_str()) < 0) if (juice_set_remote_description(mAgent.get(), string(description).c_str()) < 0)
throw std::runtime_error("Failed to parse remote SDP"); throw std::runtime_error("Failed to parse remote SDP");
} }

View File

@ -79,9 +79,10 @@ std::optional<Description> PeerConnection::remoteDescription() const {
} }
void PeerConnection::setRemoteDescription(Description description) { void PeerConnection::setRemoteDescription(Description description) {
std::lock_guard lock(mRemoteDescriptionMutex); description.hintType(localDescription() ? Description::Type::Answer : Description::Type::Offer);
auto remoteCandidates = description.extractCandidates(); auto remoteCandidates = description.extractCandidates();
std::lock_guard lock(mRemoteDescriptionMutex);
mRemoteDescription.emplace(std::move(description)); mRemoteDescription.emplace(std::move(description));
auto iceTransport = std::atomic_load(&mIceTransport); auto iceTransport = std::atomic_load(&mIceTransport);
@ -209,6 +210,7 @@ void PeerConnection::onGatheringStateChange(std::function<void(GatheringState st
} }
shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role) { shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role) {
try {
std::lock_guard lock(mInitMutex); std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mIceTransport)) if (auto transport = std::atomic_load(&mIceTransport))
return transport; return transport;
@ -250,9 +252,15 @@ shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role
}); });
std::atomic_store(&mIceTransport, transport); std::atomic_store(&mIceTransport, transport);
return transport; return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
changeState(State::Failed);
throw std::runtime_error("ICE transport initialization failed");
}
} }
shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() { shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
try {
std::lock_guard lock(mInitMutex); std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mDtlsTransport)) if (auto transport = std::atomic_load(&mDtlsTransport))
return transport; return transport;
@ -278,9 +286,15 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
}); });
std::atomic_store(&mDtlsTransport, transport); std::atomic_store(&mDtlsTransport, transport);
return transport; return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
changeState(State::Failed);
throw std::runtime_error("DTLS transport initialization failed");
}
} }
shared_ptr<SctpTransport> PeerConnection::initSctpTransport() { shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
try {
std::lock_guard lock(mInitMutex); std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mSctpTransport)) if (auto transport = std::atomic_load(&mSctpTransport))
return transport; return transport;
@ -311,6 +325,11 @@ shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
}); });
std::atomic_store(&mSctpTransport, transport); std::atomic_store(&mSctpTransport, transport);
return transport; return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
changeState(State::Failed);
throw std::runtime_error("SCTP transport initialization failed");
}
} }
void PeerConnection::endLocalCandidates() { void PeerConnection::endLocalCandidates() {

View File

@ -27,6 +27,25 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef USE_JUICE
#ifndef __APPLE__
// libjuice enables Linux path MTU discovery or sets the DF flag
#define USE_PMTUD 1
#else
// Setting the DF flag is not available on Mac OS
#define USE_PMTUD 0
#endif
#else
#ifdef __linux__
// Linux UDP does path MTU discovery by default (setting DF and returning EMSGSIZE)
// It should be safe to enable discovery for SCTP.
#define USE_PMTUD 1
#else
// Otherwise assume fragmentation
#define USE_PMTUD 0
#endif
#endif
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace std::chrono; using namespace std::chrono;
@ -119,12 +138,11 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
std::to_string(errno)); std::to_string(errno));
struct sctp_paddrparams spp = {}; struct sctp_paddrparams spp = {};
#ifdef __linux__ #if USE_PMTUD
// Linux UDP does path MTU discovery by default (setting DF and returning EMSGSIZE). // Enabled SCTP path MTU discovery
// It should be safe to enable discovery for SCTP.
spp.spp_flags = SPP_PMTUD_ENABLE; spp.spp_flags = SPP_PMTUD_ENABLE;
#else #else
// Otherwise, fall back to a safe MTU value. // Fall back to a safe MTU value.
spp.spp_flags = SPP_PMTUD_DISABLE; spp.spp_flags = SPP_PMTUD_DISABLE;
spp.spp_pathmtu = 1200; // Max safe value recommended by RFC 8261 spp.spp_pathmtu = 1200; // Max safe value recommended by RFC 8261
// See https://tools.ietf.org/html/rfc8261#section-5 // See https://tools.ietf.org/html/rfc8261#section-5
@ -355,7 +373,7 @@ bool SctpTransport::trySendMessage(message_ptr message) {
if (ret >= 0) { if (ret >= 0) {
PLOG_VERBOSE << "SCTP sent size=" << message->size(); PLOG_VERBOSE << "SCTP sent size=" << message->size();
return true; return true;
} else if (errno == EWOULDBLOCK && errno == EAGAIN) { } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
PLOG_VERBOSE << "SCTP sending not possible"; PLOG_VERBOSE << "SCTP sending not possible";
return false; return false;
} else { } else {

View File

@ -35,32 +35,21 @@ template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitLogger(LogLevel::Warning); InitLogger(LogLevel::Warning);
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
throw std::runtime_error("WSAStartup failed, error=" + std::to_string(WSAGetLastError()));
#endif
Configuration config; Configuration config;
// config.iceServers.emplace_back("stun:stun.l.google.com:19302"); // config.iceServers.emplace_back("stun:stun.l.google.com:19302");
// config.enableIceTcp = true; // config.iceServers.emplace_back(IceServer("TURN_SERVER_URL", "PORT", "USERNAME", "PASSWORD",
// IceServer::RelayType::TurnUdp)); // libnice only
// TURN server example // config.enableIceTcp = true; // libnice only
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
// IceServer::RelayType::TurnUdp);
// config.iceServers.push_back(turnServer);
auto pc1 = std::make_shared<PeerConnection>(config); auto pc1 = std::make_shared<PeerConnection>(config);
auto pc2 = std::make_shared<PeerConnection>(config); auto pc2 = std::make_shared<PeerConnection>(config);
#ifdef _WIN32
WSADATA wsaData;
int iResult;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::string err("WSAStartup failed. Error:");
err.append(WSAGetLastError() + "");
std::cout << err;
return -1;
}
#endif
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) { pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) {
auto pc2 = wpc2.lock(); auto pc2 = wpc2.lock();
if (!pc2) if (!pc2)
@ -140,20 +129,18 @@ int main(int argc, char **argv) {
if (auto addr = pc2->remoteAddress()) if (auto addr = pc2->remoteAddress())
cout << "Remote address 2: " << *addr << endl; cout << "Remote address 2: " << *addr << endl;
if (dc1->isOpen() && dc2->isOpen()) { bool success;
if ((success = dc1->isOpen() && dc2->isOpen())) {
pc1->close(); pc1->close();
pc2->close(); pc2->close();
cout << "Success" << endl; cout << "Success" << endl;
#ifdef _WIN32
WSACleanup();
#endif
return 0;
} else { } else {
cout << "Failure" << endl; cout << "Failure" << endl;
}
#ifdef _WIN32 #ifdef _WIN32
WSACleanup(); WSACleanup();
#endif #endif
return 1;
} return success ? 0 : 1;
} }

View File

@ -21,6 +21,7 @@
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <thread>
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
@ -32,60 +33,46 @@ using namespace std;
template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; } template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitLogger(LogLevel::Warning); InitLogger(LogLevel::Debug);
Configuration config;
// config.iceServers.emplace_back("stun.l.google.com:19302");
// config.enableIceTcp = true;
// TURN Server example
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
// IceServer::RelayType::TurnUdp);
// config.iceServers.push_back(turnServer);
auto pc = std::make_shared<PeerConnection>(config);
#ifdef _WIN32 #ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;
int iResult; if (WSAStartup(MAKEWORD(2, 2), &wsaData))
throw std::runtime_error("WSAStartup failed, error=" + std::to_string(WSAGetLastError()));
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::string err("WSAStartup failed. Error:");
err.append(WSAGetLastError() + "");
std::cout << err;
return -1;
}
#endif #endif
pc->onLocalDescription([](const Description &sdp) { Configuration config;
std::string s(sdp); // config.iceServers.emplace_back("stun.l.google.com:19302");
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
std::replace(s.begin(), s.end(), '\r', static_cast<char>(95)); auto pc = std::make_shared<PeerConnection>(config);
cout << "Local Description (Paste this to other peer):" << endl << s << endl << endl;
pc->onLocalDescription([](const Description &description) {
cout << "Local Description (Paste this to the other peer):" << endl;
cout << string(description) << endl;
}); });
pc->onLocalCandidate([](const Candidate &candidate) { pc->onLocalCandidate([](const Candidate &candidate) {
cout << "Local Candidate (Paste this to other peer):" << endl << candidate << endl << endl; cout << "Local Candidate (Paste this to the other peer after the local description):"
<< endl;
cout << string(candidate) << endl << endl;
}); });
pc->onStateChange( pc->onStateChange(
[](PeerConnection::State state) { cout << "[ State: " << state << " ]" << endl; }); [](PeerConnection::State state) { cout << "[State: " << state << "]" << endl; });
pc->onGatheringStateChange([](PeerConnection::GatheringState state) { pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "[ Gathering State: " << state << " ]" << endl; cout << "[Gathering State: " << state << "]" << endl;
}); });
shared_ptr<DataChannel> dc = nullptr; shared_ptr<DataChannel> dc = nullptr;
pc->onDataChannel([&](shared_ptr<DataChannel> _dc) { pc->onDataChannel([&](shared_ptr<DataChannel> _dc) {
cout << "[ Got a DataChannel with label: " << _dc->label() << " ]" << endl; cout << "[Got a DataChannel with label: " << _dc->label() << "]" << endl;
dc = _dc; dc = _dc;
dc->onClosed([&]() { cout << "[ DataChannel closed: " << dc->label() << " ]" << endl; }); dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
dc->onMessage([](const variant<binary, string> &message) { dc->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "[ Received: " << get<string>(message) << " ]" << endl; cout << "[Received message: " << get<string>(message) << "]" << endl;
} }
}); });
}); });
@ -93,69 +80,62 @@ int main(int argc, char **argv) {
bool exit = false; bool exit = false;
while (!exit) { while (!exit) {
cout << endl cout << endl
<< "**********************************************************************************"
"*****"
<< endl << endl
<< "*************************************************************************" << endl
<< "* 0: Exit /" << "* 0: Exit /"
<< " 1: Enter Description /" << " 1: Enter remote description /"
<< " 2: Enter Candidate /" << " 2: Enter remote candidate /"
<< " 3: Send Message *" << endl << " 3: Send message *" << endl
<< " [Command]: "; << "[Command]: ";
int command; int command = -1;
std::string sdp, candidate, message;
const char *a;
std::unique_ptr<Candidate> candidatePtr;
std::unique_ptr<Description> descPtr;
cin >> command; cin >> command;
cin.ignore();
switch (command) { switch (command) {
case 0: case 0: {
exit = true; exit = true;
break; break;
}
case 1: case 1: {
// Parse Description // Parse Description
cout << "[SDP]: "; cout << "[Description]: ";
sdp = ""; string sdp, line;
while (sdp.length() == 0) while (getline(cin, line) && !line.empty()) {
getline(cin, sdp); sdp += line;
sdp += "\r\n";
std::replace(sdp.begin(), sdp.end(), static_cast<char>(94), '\n'); }
std::replace(sdp.begin(), sdp.end(), static_cast<char>(95), '\r'); std::cout << sdp;
descPtr = std::make_unique<Description>(sdp, Description::Type::Offer, pc->setRemoteDescription(sdp);
Description::Role::Passive);
pc->setRemoteDescription(*descPtr);
break; break;
}
case 2: case 2: {
// Parse Candidate // Parse Candidate
cout << "[Candidate]: "; cout << "[Candidate]: ";
candidate = ""; string candidate;
while (candidate.length() == 0)
getline(cin, candidate); getline(cin, candidate);
pc->addRemoteCandidate(candidate);
candidatePtr = std::make_unique<Candidate>(candidate);
pc->addRemoteCandidate(*candidatePtr);
break; break;
}
case 3: case 3: {
// Send Message // Send Message
if (!dc || !dc->isOpen()) { if (!dc || !dc->isOpen()) {
cout << "** Channel is not Open ** "; cout << "** Channel is not Open ** ";
break; break;
} }
cout << "[Message]: "; cout << "[Message]: ";
message = ""; string message;
while (message.length() == 0)
getline(cin, message); getline(cin, message);
dc->send(message); dc->send(message);
break; break;
}
default: default: {
cout << "** Invalid Command ** "; cout << "** Invalid Command ** ";
break; break;
} }
} }
}
if (dc) if (dc)
dc->close(); dc->close();

View File

@ -21,6 +21,7 @@
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <thread>
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
@ -34,125 +35,107 @@ template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitLogger(LogLevel::Warning); InitLogger(LogLevel::Warning);
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
throw std::runtime_error("WSAStartup failed, error=" + std::to_string(WSAGetLastError()));
#endif
Configuration config; Configuration config;
// config.iceServers.emplace_back("stun.l.google.com:19302"); // config.iceServers.emplace_back("stun.l.google.com:19302");
// config.enableIceTcp = true;
// TURN server example
// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
// IceServer::RelayType::TurnUdp);
// config.iceServers.push_back(turnServer);
auto pc = std::make_shared<PeerConnection>(config); auto pc = std::make_shared<PeerConnection>(config);
#ifdef _WIN32 pc->onLocalDescription([](const Description &description) {
WSADATA wsaData; cout << "Local Description (Paste this to the other peer):" << endl;
int iResult; cout << string(description) << endl;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::string err("WSAStartup failed. Error:");
err.append(WSAGetLastError() + "");
std::cout << err;
return -1;
}
#endif
pc->onLocalDescription([](const Description &sdp) {
std::string s(sdp);
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
std::replace(s.begin(), s.end(), '\r', static_cast<char>(95));
cout << "Local Description (Paste this to other peer):" << endl << s << endl << endl;
}); });
pc->onLocalCandidate([](const Candidate &candidate) { pc->onLocalCandidate([](const Candidate &candidate) {
cout << "Local Candidate (Paste this to other peer):" << endl << candidate << endl << endl; cout << "Local Candidate (Paste this to the other peer after the local description):"
<< endl;
cout << string(candidate) << endl << endl;
}); });
pc->onStateChange( pc->onStateChange(
[](PeerConnection::State state) { cout << "[ State: " << state << " ]" << endl; }); [](PeerConnection::State state) { cout << "[State: " << state << "]" << endl; });
pc->onGatheringStateChange([](PeerConnection::GatheringState state) { pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "[ Gathering State: " << state << " ]" << endl; cout << "[Gathering State: " << state << "]" << endl;
}); });
auto dc = pc->createDataChannel("test"); auto dc = pc->createDataChannel("test"); // this is the offerer, so create a data channel
dc->onOpen([&]() { cout << "[ DataChannel open: " << dc->label() << " ]" << endl; });
dc->onClosed([&]() { cout << "[ DataChannel closed: " << dc->label() << " ]" << endl; }); dc->onOpen([&]() { cout << "[DataChannel open: " << dc->label() << "]" << endl; });
dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
dc->onMessage([](const variant<binary, string> &message) { dc->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "[ Received: " << get<string>(message) << " ]" << endl; cout << "[Received: " << get<string>(message) << "]" << endl;
} }
}); });
this_thread::sleep_for(1s);
bool exit = false; bool exit = false;
while (!exit) { while (!exit) {
cout << endl cout << endl
<< "**********************************************************************************"
"*****"
<< endl << endl
<< "*************************************************************************" << endl
<< "* 0: Exit /" << "* 0: Exit /"
<< " 1: Enter Description /" << " 1: Enter remote description /"
<< " 2: Enter Candidate /" << " 2: Enter remote candidate /"
<< " 3: Send Message *" << endl << " 3: Send message *" << endl
<< " [Command]: "; << "[Command]: ";
int command; int command = -1;
std::string sdp, candidate, message;
const char *a;
std::unique_ptr<Candidate> candidatePtr;
std::unique_ptr<Description> descPtr;
cin >> command; cin >> command;
cin.ignore();
switch (command) { switch (command) {
case 0: case 0: {
exit = true; exit = true;
break; break;
}
case 1: case 1: {
// Parse Description // Parse Description
cout << "[SDP]: "; cout << "[Description]: ";
sdp = ""; string sdp, line;
while (sdp.length() == 0) while (getline(cin, line) && !line.empty()) {
getline(cin, sdp); sdp += line;
sdp += "\r\n";
std::replace(sdp.begin(), sdp.end(), static_cast<char>(94), '\n'); }
std::replace(sdp.begin(), sdp.end(), static_cast<char>(95), '\r'); pc->setRemoteDescription(sdp);
descPtr = std::make_unique<Description>(sdp);
pc->setRemoteDescription(*descPtr);
break; break;
}
case 2: case 2: {
// Parse Candidate // Parse Candidate
cout << "[Candidate]: "; cout << "[Candidate]: ";
candidate = ""; string candidate;
while (candidate.length() == 0)
getline(cin, candidate); getline(cin, candidate);
pc->addRemoteCandidate(candidate);
candidatePtr = std::make_unique<Candidate>(candidate);
pc->addRemoteCandidate(*candidatePtr);
break; break;
}
case 3: case 3: {
// Send Message // Send Message
if (!dc->isOpen()) { if (!dc->isOpen()) {
cout << "** Channel is not Open ** "; cout << "** Channel is not Open ** ";
break; break;
} }
cout << "[Message]: "; cout << "[Message]: ";
message = ""; string message;
while (message.length() == 0)
getline(cin, message); getline(cin, message);
dc->send(message); dc->send(message);
break; break;
}
default: default: {
cout << "** Invalid Command ** "; cout << "** Invalid Command ** ";
break; break;
} }
} }
}
if (dc) if (dc)
dc->close(); dc->close();