mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
dc065add0b | |||
e64d4049a6 | |||
cb3bc85474 | |||
7af3da7872 | |||
3c77d717d2 | |||
6f399945fe | |||
c8b14b1262 | |||
35d4455c4f | |||
7d21b4b42b | |||
24e9e06c5a | |||
443a19d8e7 | |||
83de743924 | |||
1dc1de4b86 | |||
8ca7722d48 | |||
3079072e63 | |||
982d1c10e1 | |||
50b22bbf3c | |||
93e153398f |
@ -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.2
|
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)
|
||||||
@ -16,6 +16,12 @@ 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)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
if (MSYS OR MINGW)
|
||||||
|
add_definitions(-DSCTP_STDINT_INCLUDE=<stdint.h>)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(LIBDATACHANNEL_SOURCES
|
set(LIBDATACHANNEL_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/candidate.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/candidate.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/certificate.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/certificate.cpp
|
||||||
@ -62,6 +68,12 @@ target_link_libraries(datachannel
|
|||||||
Threads::Threads
|
Threads::Threads
|
||||||
Usrsctp::UsrsctpStatic
|
Usrsctp::UsrsctpStatic
|
||||||
)
|
)
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel
|
||||||
|
"wsock32" #winsock2
|
||||||
|
"ws2_32" #winsock2
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
||||||
set_target_properties(datachannel-static PROPERTIES
|
set_target_properties(datachannel-static PROPERTIES
|
||||||
@ -76,6 +88,12 @@ target_link_libraries(datachannel-static
|
|||||||
Threads::Threads
|
Threads::Threads
|
||||||
Usrsctp::UsrsctpStatic
|
Usrsctp::UsrsctpStatic
|
||||||
)
|
)
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-static
|
||||||
|
"wsock32" #winsock2
|
||||||
|
"ws2_32" #winsock2
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (USE_GNUTLS)
|
if (USE_GNUTLS)
|
||||||
find_package(GnuTLS REQUIRED)
|
find_package(GnuTLS REQUIRED)
|
||||||
@ -138,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)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# libdatachannel - C/C++ WebRTC DataChannels
|
# libdatachannel - C/C++ WebRTC DataChannels
|
||||||
|
|
||||||
libdatachannel is a standalone implementation of WebRTC DataChannels in C++17 with C bindings. It enables direct connectivity between native applications and web browsers without the pain of importing the entire WebRTC stack. Its API is modelled as a simplified version of the JavaScript WebRTC API, in order to ease the design of cross-environment applications.
|
libdatachannel is a standalone implementation of WebRTC DataChannels in C++17 with C bindings for POSIX platforms and Microsoft Windows. It enables direct connectivity between native applications and web browsers without the pain of importing the entire WebRTC stack. Its API is modelled as a simplified version of the JavaScript WebRTC API, in order to ease the design of cross-environment applications.
|
||||||
|
|
||||||
This projet is originally inspired by [librtcdcpp](https://github.com/chadnickbok/librtcdcpp), however it is a complete rewrite from scratch, because the messy architecture of librtcdcpp made solving its implementation issues difficult.
|
This projet is originally inspired by [librtcdcpp](https://github.com/chadnickbok/librtcdcpp), however it is a complete rewrite from scratch, because the messy architecture of librtcdcpp made solving its implementation issues difficult.
|
||||||
|
|
||||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: 65f492dbff...856475ebd1
@ -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);
|
||||||
|
@ -19,6 +19,12 @@
|
|||||||
#ifndef RTC_INCLUDE_H
|
#ifndef RTC_INCLUDE_H
|
||||||
#define RTC_INCLUDE_H
|
#define RTC_INCLUDE_H
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x0602
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -22,8 +22,14 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#elif __linux__
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
using std::array;
|
using std::array;
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -19,9 +19,14 @@
|
|||||||
#include "icetransport.hpp"
|
#include "icetransport.hpp"
|
||||||
#include "configuration.hpp"
|
#include "configuration.hpp"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#elif __linux__
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -109,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");
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -23,7 +23,28 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#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;
|
||||||
@ -117,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
|
||||||
@ -353,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 {
|
||||||
|
@ -29,7 +29,12 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#elif __linux__
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "usrsctp.h"
|
#include "usrsctp.h"
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -31,14 +35,17 @@ 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);
|
||||||
@ -122,14 +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;
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
cout << "Failure" << endl;
|
cout << "Failure" << endl;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return success ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,11 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -28,27 +33,28 @@ 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);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
pc->onLocalDescription([](const Description &sdp) {
|
pc->onLocalDescription([](const Description &description) {
|
||||||
std::string s(sdp);
|
cout << "Local Description (Paste this to the other peer):" << endl;
|
||||||
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
|
cout << string(description) << endl;
|
||||||
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(
|
||||||
@ -66,7 +72,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -74,71 +80,69 @@ 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');
|
}
|
||||||
descPtr = std::make_unique<Description>(sdp, Description::Type::Offer,
|
std::cout << sdp;
|
||||||
Description::Role::Passive);
|
pc->setRemoteDescription(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 || !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();
|
||||||
if (pc)
|
if (pc)
|
||||||
pc->close();
|
pc->close();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,11 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -30,25 +35,26 @@ 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);
|
||||||
|
|
||||||
pc->onLocalDescription([](const Description &sdp) {
|
pc->onLocalDescription([](const Description &description) {
|
||||||
std::string s(sdp);
|
cout << "Local Description (Paste this to the other peer):" << endl;
|
||||||
std::replace(s.begin(), s.end(), '\n', static_cast<char>(94));
|
cout << string(description) << endl;
|
||||||
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(
|
||||||
@ -58,7 +64,8 @@ int main(int argc, char **argv) {
|
|||||||
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->onOpen([&]() { cout << "[DataChannel open: " << dc->label() << "]" << endl; });
|
||||||
|
|
||||||
dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
|
dc->onClosed([&]() { cout << "[DataChannel closed: " << dc->label() << "]" << endl; });
|
||||||
@ -69,73 +76,73 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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');
|
}
|
||||||
descPtr = std::make_unique<Description>(sdp);
|
pc->setRemoteDescription(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();
|
||||||
if (pc)
|
if (pc)
|
||||||
pc->close();
|
pc->close();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user