Merge pull request #347 from paullouisageneau/interface-refactoring

Refactor interface
This commit is contained in:
Paul-Louis Ageneau
2021-02-28 18:24:37 +01:00
committed by GitHub
79 changed files with 3198 additions and 2622 deletions

View File

@ -46,26 +46,17 @@ endif()
set(LIBDATACHANNEL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/candidate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/certificate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/channel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dtlssrtptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dtlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/icetransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/init.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/log.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/message.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/peerconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/logcounter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpreceivingsession.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/sctptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/threadpool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tls.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/processor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/websocket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
@ -78,36 +69,7 @@ set(LIBDATACHANNEL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerelement.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerrootelement.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpnackresponder.cpp
)
set(LIBDATACHANNEL_PRIVATE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/certificate.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dtlssrtptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dtlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/icetransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/logcounter.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/sctptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/threadpool.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tls.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/processor.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/transport.hpp
)
set(LIBDATACHANNEL_WEBSOCKET_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tcptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/verifiedtlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/websocket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/wstransport.cpp
)
set(LIBDATACHANNEL_WEBSOCKET_PRIVATE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/base64.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tcptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/verifiedtlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/wstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
)
set(LIBDATACHANNEL_HEADERS
@ -119,12 +81,11 @@ set(LIBDATACHANNEL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/description.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandler.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/include.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/init.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/log.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/queue.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.hpp
@ -145,6 +106,51 @@ set(LIBDATACHANNEL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpnackresponder.hpp
)
set(LIBDATACHANNEL_IMPL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/certificate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/channel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/datachannel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/threadpool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tls.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/track.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/processor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/verifiedtlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/websocket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/wstransport.cpp
)
set(LIBDATACHANNEL_IMPL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/certificate.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/channel.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/datachannel.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/queue.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/threadpool.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tls.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/track.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/processor.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/base64.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/verifiedtlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/websocket.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/wstransport.hpp
)
set(TESTS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
@ -163,7 +169,8 @@ set(TESTS_UWP_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SmallLogo44x44.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SplashScreen.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/StoreLogo.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Windows_TemporaryKey.pfx)
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Windows_TemporaryKey.pfx
)
set(BENCHMARK_UWP_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Logo.png
@ -172,7 +179,8 @@ set(BENCHMARK_UWP_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SmallLogo44x44.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SplashScreen.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/StoreLogo.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Windows_TemporaryKey.pfx)
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Windows_TemporaryKey.pfx
)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
@ -192,33 +200,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif()
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
if (NO_WEBSOCKET)
add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_PRIVATE_HEADERS}
${LIBDATACHANNEL_HEADERS})
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_PRIVATE_HEADERS}
${LIBDATACHANNEL_HEADERS})
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
else()
add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_PRIVATE_HEADERS}
${LIBDATACHANNEL_WEBSOCKET_SOURCES}
${LIBDATACHANNEL_WEBSOCKET_PRIVATE_HEADERS}
${LIBDATACHANNEL_HEADERS})
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_PRIVATE_HEADERS}
${LIBDATACHANNEL_WEBSOCKET_SOURCES}
${LIBDATACHANNEL_WEBSOCKET_PRIVATE_HEADERS}
${LIBDATACHANNEL_HEADERS})
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
endif()
add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_HEADERS}
${LIBDATACHANNEL_IMPL_SOURCES}
${LIBDATACHANNEL_IMPL_HEADERS})
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_HEADERS}
${LIBDATACHANNEL_IMPL_SOURCES}
${LIBDATACHANNEL_IMPL_HEADERS})
set_target_properties(datachannel PROPERTIES
VERSION ${PROJECT_VERSION}
@ -244,6 +235,14 @@ if(WIN32)
target_link_libraries(datachannel-static PUBLIC ws2_32) # winsock2
endif()
if (NO_WEBSOCKET)
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
else()
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
endif()
if(NO_MEDIA)
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=0)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=0)

View File

@ -145,50 +145,49 @@ Additionnaly, you might want to have a look at the [C API](https://github.com/pa
rtc::Configuration config;
config.iceServers.emplace_back("mystunserver.org:3478");
auto pc = make_shared<rtc::PeerConnection>(config);
rtc::PeerConection pc(config);
pc->onLocalDescription([](rtc::Description sdp) {
pc.onLocalDescription([](rtc::Description sdp) {
// Send the SDP to the remote peer
MY_SEND_DESCRIPTION_TO_REMOTE(string(sdp));
});
pc->onLocalCandidate([](rtc::Candidate candidate) {
pc.onLocalCandidate([](rtc::Candidate candidate) {
// Send the candidate to the remote peer
MY_SEND_CANDIDATE_TO_REMOTE(candidate.candidate(), candidate.mid());
});
MY_ON_RECV_DESCRIPTION_FROM_REMOTE([pc](string sdp) {
pc->setRemoteDescription(rtc::Description(sdp));
MY_ON_RECV_DESCRIPTION_FROM_REMOTE([&pc](string sdp) {
pc.setRemoteDescription(rtc::Description(sdp));
});
MY_ON_RECV_CANDIDATE_FROM_REMOTE([pc](string candidate, string mid) {
pc->addRemoteCandidate(rtc::Candidate(candidate, mid));
MY_ON_RECV_CANDIDATE_FROM_REMOTE([&pc](string candidate, string mid) {
pc.addRemoteCandidate(rtc::Candidate(candidate, mid));
});
```
### Observe the PeerConnection state
```cpp
pc->onStateChange([](PeerConnection::State state) {
pc.onStateChange([](PeerConnection::State state) {
cout << "State: " << state << endl;
});
pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state: " << state << endl;
});
```
### Create a DataChannel
```cpp
auto dc = pc->createDataChannel("test");
auto dc = pc.createDataChannel("test");
dc->onOpen([]() {
dc.onOpen([]() {
cout << "Open" << endl;
});
dc->onMessage([](variant<binary, string> message) {
dc.onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) {
cout << "Received: " << get<string>(message) << endl;
}
@ -199,30 +198,28 @@ dc->onMessage([](variant<binary, string> message) {
```cpp
shared_ptr<rtc::DataChannel> dc;
pc->onDataChannel([&dc](shared_ptr<rtc::DataChannel> incoming) {
pc.onDataChannel([&dc](shared_ptr<rtc::DataChannel> incoming) {
dc = incoming;
dc->send("Hello world!");
});
```
### Open a WebSocket
```cpp
auto ws = make_shared<rtc::WebSocket>();
rtc::WebSocket ws;
ws->onOpen([]() {
ws.onOpen([]() {
cout << "WebSocket open" << endl;
});
ws->onMessage([](variant<binary, string> message) {
ws.onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) {
cout << "WebSocket received: " << get<string>(message) << endl;
}
});
ws->open("wss://my.websocket/service");
ws.open("wss://my.websocket/service");
```
## External resources

View File

@ -54,20 +54,20 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
string randomId(size_t length);
int main(int argc, char **argv) try {
auto params = std::make_unique<Cmdline>(argc, argv);
Cmdline params(argc, argv);
rtc::InitLogger(LogLevel::Info);
Configuration config;
string stunServer = "";
if (params->noStun()) {
if (params.noStun()) {
cout << "No STUN server is configured. Only local hosts and public IP addresses supported."
<< endl;
} else {
if (params->stunServer().substr(0, 5).compare("stun:") != 0) {
if (params.stunServer().substr(0, 5).compare("stun:") != 0) {
stunServer = "stun:";
}
stunServer += params->stunServer() + ":" + to_string(params->stunPort());
stunServer += params.stunServer() + ":" + to_string(params.stunPort());
cout << "Stun server is " << stunServer << endl;
config.iceServers.emplace_back(stunServer);
}
@ -129,11 +129,11 @@ int main(int argc, char **argv) try {
});
string wsPrefix = "";
if (params->webSocketServer().substr(0, 5).compare("ws://") != 0) {
if (params.webSocketServer().substr(0, 5).compare("ws://") != 0) {
wsPrefix = "ws://";
}
const string url = wsPrefix + params->webSocketServer() + ":" +
to_string(params->webSocketPort()) + "/" + localId;
const string url = wsPrefix + params.webSocketServer() + ":" +
to_string(params.webSocketPort()) + "/" + localId;
cout << "Url is " << url << endl;
ws->open(url);
@ -251,3 +251,4 @@ string randomId(size_t length) {
generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); });
return id;
}

View File

@ -19,7 +19,7 @@
#ifndef RTC_CANDIDATE_H
#define RTC_CANDIDATE_H
#include "include.hpp"
#include "common.hpp"
#include <string>

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,7 +19,7 @@
#ifndef RTC_CHANNEL_H
#define RTC_CHANNEL_H
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
#include <atomic>
@ -28,10 +28,13 @@
namespace rtc {
class RTC_CPP_EXPORT Channel {
namespace impl {
struct Channel;
}
class RTC_CPP_EXPORT Channel : private CheshireCat<impl::Channel> {
public:
Channel() = default;
virtual ~Channel() = default;
virtual ~Channel();
virtual void close() = 0;
virtual bool send(message_variant data) = 0; // returns false if buffered
@ -54,30 +57,13 @@ public:
void setBufferedAmountLowThreshold(size_t amount);
// Extended API
virtual std::optional<message_variant> receive() = 0; // only if onMessage unset
virtual std::optional<message_variant> peek() = 0; // only if onMessage unset
virtual size_t availableAmount() const; // total size available to receive
std::optional<message_variant> receive(); // only if onMessage unset
std::optional<message_variant> peek(); // only if onMessage unset
size_t availableAmount() const; // total size available to receive
void onAvailable(std::function<void()> callback);
protected:
virtual void triggerOpen();
virtual void triggerClosed();
virtual void triggerError(string error);
virtual void triggerAvailable(size_t count);
virtual void triggerBufferedAmount(size_t amount);
void resetCallbacks();
private:
synchronized_callback<> mOpenCallback;
synchronized_callback<> mClosedCallback;
synchronized_callback<string> mErrorCallback;
synchronized_callback<message_variant> mMessageCallback;
synchronized_callback<> mAvailableCallback;
synchronized_callback<> mBufferedAmountLowCallback;
std::atomic<size_t> mBufferedAmount = 0;
std::atomic<size_t> mBufferedAmountLowThreshold = 0;
Channel(impl_ptr<impl::Channel> impl);
};
} // namespace rtc

75
include/rtc/common.hpp Normal file
View File

@ -0,0 +1,75 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_COMMON_H
#define RTC_COMMON_H
#ifndef RTC_ENABLE_MEDIA
#define RTC_ENABLE_MEDIA 1
#endif
#ifndef RTC_ENABLE_WEBSOCKET
#define RTC_ENABLE_WEBSOCKET 1
#endif
#ifdef _WIN32
#define RTC_CPP_EXPORT __declspec(dllexport)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0602 // Windows 8
#endif
#ifdef _MSC_VER
#pragma warning(disable : 4251) // disable "X needs to have dll-interface..."
#endif
#else
#define RTC_CPP_EXPORT
#endif
#include "log.hpp"
#include "utils.hpp"
#include <cstddef>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace rtc {
using std::byte;
using std::nullopt;
using std::shared_ptr;
using std::string;
using std::string_view;
using std::unique_ptr;
using std::weak_ptr;
using binary = std::vector<byte>;
using binary_ptr = std::shared_ptr<binary>;
using std::size_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::uint8_t;
} // namespace rtc
#endif

View File

@ -19,7 +19,7 @@
#ifndef RTC_ICE_CONFIGURATION_H
#define RTC_ICE_CONFIGURATION_H
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
#include <vector>

View File

@ -20,28 +20,30 @@
#define RTC_DATA_CHANNEL_H
#include "channel.hpp"
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
#include "queue.hpp"
#include "reliability.hpp"
#include <atomic>
#include <chrono>
#include <functional>
#include <shared_mutex>
#include <type_traits>
#include <variant>
#include <shared_mutex>
namespace rtc {
class SctpTransport;
class PeerConnection;
namespace impl {
class RTC_CPP_EXPORT DataChannel : public std::enable_shared_from_this<DataChannel>,
public Channel {
struct DataChannel;
struct PeerConnection;
} // namespace impl
class RTC_CPP_EXPORT DataChannel final : private CheshireCat<impl::DataChannel>, public Channel {
public:
DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
Reliability reliability);
DataChannel(impl_ptr<impl::DataChannel> impl);
virtual ~DataChannel();
uint16_t stream() const;
@ -50,60 +52,18 @@ public:
string protocol() const;
Reliability reliability() const;
bool isOpen(void) const override;
bool isClosed(void) const override;
size_t maxMessageSize() const override;
void close(void) override;
bool send(message_variant data) override;
bool send(const byte *data, size_t size) override;
template <typename Buffer> bool sendBuffer(const Buffer &buf);
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
bool isOpen(void) const override;
bool isClosed(void) const override;
size_t maxMessageSize() const override;
// Extended API
size_t availableAmount() const override;
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
protected:
virtual void open(std::shared_ptr<SctpTransport> transport);
virtual void processOpenMessage(message_ptr message);
void remoteClose();
bool outgoing(message_ptr message);
void incoming(message_ptr message);
const std::weak_ptr<PeerConnection> mPeerConnection;
std::weak_ptr<SctpTransport> mSctpTransport;
uint16_t mStream;
string mLabel;
string mProtocol;
std::shared_ptr<Reliability> mReliability;
mutable std::shared_mutex mMutex;
std::atomic<bool> mIsOpen = false;
std::atomic<bool> mIsClosed = false;
private:
Queue<message_ptr> mRecvQueue;
friend class PeerConnection;
};
class RTC_CPP_EXPORT NegotiatedDataChannel final : public DataChannel {
public:
NegotiatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
string protocol, Reliability reliability);
NegotiatedDataChannel(std::weak_ptr<PeerConnection> pc, std::weak_ptr<SctpTransport> transport,
uint16_t stream);
~NegotiatedDataChannel();
private:
void open(std::shared_ptr<SctpTransport> transport) override;
void processOpenMessage(message_ptr message) override;
friend class PeerConnection;
using CheshireCat<impl::DataChannel>::impl;
};
template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer &buf) {
@ -115,9 +75,7 @@ template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer
template <typename Buffer> bool DataChannel::sendBuffer(const Buffer &buf) {
auto [bytes, size] = to_bytes(buf);
auto message = std::make_shared<Message>(size);
std::copy(bytes, bytes + size, message->data());
return outgoing(message);
return send(bytes, size);
}
template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterator last) {
@ -125,13 +83,13 @@ template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterat
for (Iterator it = first; it != last; ++it)
size += it->size();
auto message = std::make_shared<Message>(size);
auto pos = message->begin();
binary buffer(size);
byte *pos = buffer.data();
for (Iterator it = first; it != last; ++it) {
auto [bytes, len] = to_bytes(*it);
pos = std::copy(bytes, bytes + len, pos);
}
return outgoing(message);
return send(std::move(buffer));
}
} // namespace rtc

View File

@ -21,7 +21,7 @@
#define RTC_DESCRIPTION_H
#include "candidate.hpp"
#include "include.hpp"
#include "common.hpp"
#include <iostream>
#include <map>

View File

@ -19,7 +19,7 @@
#ifndef RTC_INIT_H
#define RTC_INIT_H
#include "include.hpp"
#include "common.hpp"
#include <mutex>

View File

@ -35,7 +35,7 @@
#pragma warning(pop)
#endif
#include "include.hpp"
#include "common.hpp"
namespace rtc {

View File

@ -20,7 +20,7 @@
#ifndef RTC_MEDIA_HANDLER_H
#define RTC_MEDIA_HANDLER_H
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
namespace rtc {

View File

@ -20,7 +20,7 @@
#if RTC_ENABLE_MEDIA
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
#include "rtp.hpp"

View File

@ -19,7 +19,7 @@
#ifndef RTC_MESSAGE_H
#define RTC_MESSAGE_H
#include "include.hpp"
#include "common.hpp"
#include "reliability.hpp"
#include <functional>

View File

@ -21,7 +21,7 @@
#if RTC_ENABLE_MEDIA
#include "include.hpp"
#include "common.hpp"
namespace rtc {

View File

@ -23,33 +23,23 @@
#include "configuration.hpp"
#include "datachannel.hpp"
#include "description.hpp"
#include "include.hpp"
#include "common.hpp"
#include "init.hpp"
#include "message.hpp"
#include "reliability.hpp"
#include "rtc.hpp"
#include "track.hpp"
#include <atomic>
#include <chrono>
#include <functional>
#include <future>
#include <list>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <unordered_map>
namespace rtc {
class Certificate;
class Processor;
class IceTransport;
class DtlsTransport;
class SctpTransport;
namespace impl {
using certificate_ptr = std::shared_ptr<Certificate>;
using future_certificate_ptr = std::shared_future<certificate_ptr>;
struct PeerConnection;
}
struct RTC_CPP_EXPORT DataChannelInit {
Reliability reliability = {};
@ -58,7 +48,7 @@ struct RTC_CPP_EXPORT DataChannelInit {
string protocol = "";
};
class RTC_CPP_EXPORT PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
public:
enum class State : int {
New = RTC_NEW,
@ -84,7 +74,7 @@ public:
} rtcSignalingState;
PeerConnection();
PeerConnection(const Configuration &config);
PeerConnection(Configuration config);
~PeerConnection();
void close();
@ -128,80 +118,6 @@ public:
// Track media support requires compiling with libSRTP
std::shared_ptr<Track> addTrack(Description::Media description);
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
private:
std::shared_ptr<IceTransport> initIceTransport();
std::shared_ptr<DtlsTransport> initDtlsTransport();
std::shared_ptr<SctpTransport> initSctpTransport();
void closeTransports();
void endLocalCandidates();
bool checkFingerprint(const std::string &fingerprint) const;
void forwardMessage(message_ptr message);
void forwardMedia(message_ptr message);
void forwardBufferedAmount(uint16_t stream, size_t amount);
std::optional<std::string> getMidFromSsrc(uint32_t ssrc);
std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
DataChannelInit init);
std::shared_ptr<DataChannel> findDataChannel(uint16_t stream);
void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
void openDataChannels();
void closeDataChannels();
void remoteCloseDataChannels();
void incomingTrack(Description::Media description);
void openTracks();
void validateRemoteDescription(const Description &description);
void processLocalDescription(Description description);
void processLocalCandidate(Candidate candidate);
void processRemoteDescription(Description description);
void processRemoteCandidate(Candidate candidate);
string localBundleMid() const;
void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
void triggerTrack(std::shared_ptr<Track> track);
bool changeState(State state);
bool changeGatheringState(GatheringState state);
bool changeSignalingState(SignalingState state);
void resetCallbacks();
void outgoingMedia(message_ptr message);
const init_token mInitToken = Init::Token();
const Configuration mConfig;
const future_certificate_ptr mCertificate;
const std::unique_ptr<Processor> mProcessor;
std::optional<Description> mLocalDescription, mRemoteDescription;
std::optional<Description> mCurrentLocalDescription;
mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
std::shared_ptr<IceTransport> mIceTransport;
std::shared_ptr<DtlsTransport> mDtlsTransport;
std::shared_ptr<SctpTransport> mSctpTransport;
std::unordered_map<uint16_t, std::weak_ptr<DataChannel>> mDataChannels; // by stream ID
std::unordered_map<string, std::weak_ptr<Track>> mTracks; // by mid
std::vector<std::weak_ptr<Track>> mTrackLines; // by SDP order
std::shared_mutex mDataChannelsMutex, mTracksMutex;
std::unordered_map<uint32_t, string> mMidFromSsrc; // cache
std::atomic<State> mState;
std::atomic<GatheringState> mGatheringState;
std::atomic<SignalingState> mSignalingState;
std::atomic<bool> mNegotiationNeeded;
synchronized_callback<std::shared_ptr<DataChannel>> mDataChannelCallback;
synchronized_callback<Description> mLocalDescriptionCallback;
synchronized_callback<Candidate> mLocalCandidateCallback;
synchronized_callback<State> mStateChangeCallback;
synchronized_callback<GatheringState> mGatheringStateChangeCallback;
synchronized_callback<SignalingState> mSignalingStateChangeCallback;
synchronized_callback<std::shared_ptr<Track>> mTrackCallback;
};
} // namespace rtc

View File

@ -19,7 +19,7 @@
#ifndef RTC_RELIABILITY_H
#define RTC_RELIABILITY_H
#include "include.hpp"
#include "common.hpp"
#include <chrono>
#include <variant>

View File

@ -17,7 +17,7 @@
*/
// C++ API
#include "include.hpp"
#include "common.hpp"
#include "init.hpp" // for rtc::Cleanup()
#include "log.hpp"
//

View File

@ -22,7 +22,7 @@
#if RTC_ENABLE_MEDIA
#include "include.hpp"
#include "common.hpp"
#include "mediahandler.hpp"
#include "message.hpp"
#include "rtp.hpp"

View File

@ -21,29 +21,30 @@
#include "channel.hpp"
#include "description.hpp"
#include "include.hpp"
#include "message.hpp"
#include "queue.hpp"
#include "common.hpp"
#include "mediahandler.hpp"
#include "message.hpp"
#include <atomic>
#include <variant>
#include <shared_mutex>
#include <variant>
namespace rtc {
#if RTC_ENABLE_MEDIA
class DtlsSrtpTransport;
#endif
namespace impl {
class RTC_CPP_EXPORT Track final : public std::enable_shared_from_this<Track>, public Channel {
class Track;
} // namespace impl
class RTC_CPP_EXPORT Track final : private CheshireCat<impl::Track>, public Channel {
public:
Track(Description::Media description);
Track(impl_ptr<impl::Track> impl);
~Track() = default;
string mid() const;
Description::Media description() const;
Description::Direction direction() const;
Description::Media description() const;
void setDescription(Description::Media description);
@ -55,11 +56,6 @@ public:
bool isClosed(void) const override;
size_t maxMessageSize() const override;
// Extended API
size_t availableAmount() const override;
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
bool requestKeyframe();
// RTCP handler
@ -67,24 +63,7 @@ public:
std::shared_ptr<MediaHandler> getRtcpHandler();
private:
#if RTC_ENABLE_MEDIA
void open(std::shared_ptr<DtlsSrtpTransport> transport);
std::weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
#endif
void incoming(message_ptr message);
bool outgoing(message_ptr message);
Description::Media mMediaDescription;
std::shared_ptr<MediaHandler> mRtcpHandler;
mutable std::shared_mutex mMutex;
std::atomic<bool> mIsClosed = false;
Queue<message_ptr> mRecvQueue;
friend class PeerConnection;
using CheshireCat<impl::Track>::impl;
};
} // namespace rtc

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,69 +16,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_INCLUDE_H
#define RTC_INCLUDE_H
#ifndef RTC_UTILS_H
#define RTC_UTILS_H
#ifndef RTC_ENABLE_MEDIA
#define RTC_ENABLE_MEDIA 1
#endif
#ifndef RTC_ENABLE_WEBSOCKET
#define RTC_ENABLE_WEBSOCKET 1
#endif
#ifdef _WIN32
#define RTC_CPP_EXPORT __declspec(dllexport)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0602 // Windows 8
#endif
#ifdef _MSC_VER
#pragma warning(disable : 4251) // disable "X needs to have dll-interface..."
#endif
#else
#define RTC_CPP_EXPORT
#endif
#include "log.hpp"
#include <cstddef>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace rtc {
using std::byte;
using std::string;
using std::string_view;
using binary = std::vector<byte>;
using binary_ptr = std::shared_ptr<binary>;
using std::nullopt;
using std::size_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::uint8_t;
const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length
const size_t MAX_NUMERICSERV_LEN = 6; // Max port string representation length
const uint16_t DEFAULT_SCTP_PORT = 5000; // SCTP port to use by default
const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536; // Remote max message size if not specified in SDP
const size_t LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Local max message size
const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
const int THREADPOOL_SIZE = 4; // Number of threads in the global thread pool
const size_t DEFAULT_IPV4_MTU = 1200; // IPv4 safe MTU value recommended by RFC 8261
// overloaded helper
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
@ -95,7 +41,7 @@ template <typename F, typename T, typename... Args> auto weak_bind(F &&f, T *t,
}
// scope_guard helper
class scope_guard {
class scope_guard final {
public:
scope_guard(std::function<void()> func) : function(std::move(func)) {}
scope_guard(scope_guard &&other) = delete;
@ -111,7 +57,8 @@ private:
std::function<void()> function;
};
template <typename... Args> class synchronized_callback {
// callback with built-in synchronization
template <typename... Args> class synchronized_callback final {
public:
synchronized_callback() = default;
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
@ -157,6 +104,33 @@ private:
std::function<void(Args...)> callback;
mutable std::recursive_mutex mutex;
};
// pimpl base class
template <typename T> using impl_ptr = std::shared_ptr<T>;
template <typename T> class CheshireCat {
public:
CheshireCat(impl_ptr<T> impl) : mImpl(std::move(impl)) {}
template <typename... Args>
CheshireCat(Args... args) : mImpl(std::make_shared<T>(std::move(args)...)) {}
CheshireCat(CheshireCat<T> &&cc) { *this = std::move(cc); }
CheshireCat(const CheshireCat<T> &) = delete;
virtual ~CheshireCat() = default;
CheshireCat &operator=(CheshireCat<T> &&cc) {
mImpl = std::move(cc.mImpl);
return *this;
};
CheshireCat &operator=(const CheshireCat<T> &) = delete;
protected:
impl_ptr<T> impl() { return mImpl; }
impl_ptr<const T> impl() const { return mImpl; }
private:
impl_ptr<T> mImpl;
};
} // namespace rtc
#endif

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -22,24 +22,18 @@
#if RTC_ENABLE_WEBSOCKET
#include "channel.hpp"
#include "include.hpp"
#include "init.hpp"
#include "common.hpp"
#include "message.hpp"
#include "queue.hpp"
#include <atomic>
#include <optional>
#include <thread>
#include <variant>
namespace rtc {
class TcpTransport;
class TlsTransport;
class WsTransport;
namespace impl {
class RTC_CPP_EXPORT WebSocket final : public Channel,
public std::enable_shared_from_this<WebSocket> {
struct WebSocket;
}
class RTC_CPP_EXPORT WebSocket final : private CheshireCat<impl::WebSocket>, public Channel {
public:
enum class State : int {
Connecting = 0,
@ -53,49 +47,25 @@ public:
std::vector<string> protocols;
};
WebSocket(std::optional<Configuration> config = nullopt);
WebSocket();
WebSocket(Configuration config);
~WebSocket();
State readyState() const;
bool isOpen() const override;
bool isClosed() const override;
size_t maxMessageSize() const override;
void open(const string &url);
void close() override;
bool send(const message_variant data) override;
bool send(const byte *data, size_t size) override;
bool isOpen() const override;
bool isClosed() const override;
size_t maxMessageSize() const override;
// Extended API
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
size_t availableAmount() const override; // total size available to receive
private:
bool changeState(State state);
void remoteClose();
bool outgoing(message_ptr message);
void incoming(message_ptr message);
std::shared_ptr<TcpTransport> initTcpTransport();
std::shared_ptr<TlsTransport> initTlsTransport();
std::shared_ptr<WsTransport> initWsTransport();
void closeTransports();
init_token mInitToken = Init::Token();
std::shared_ptr<TcpTransport> mTcpTransport;
std::shared_ptr<TlsTransport> mTlsTransport;
std::shared_ptr<WsTransport> mWsTransport;
std::recursive_mutex mInitMutex;
const Configuration mConfig;
string mScheme, mHost, mHostname, mService, mPath;
std::atomic<State> mState = State::Closed;
Queue<message_ptr> mRecvQueue;
using CheshireCat<impl::WebSocket>::impl;
};
} // namespace rtc
#endif

View File

@ -17,6 +17,7 @@
*/
#include "candidate.hpp"
#include "globals.hpp"
#include <algorithm>
#include <array>

View File

@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "include.hpp"
#include "common.hpp"
#include "rtc.h"
@ -43,8 +43,6 @@
using namespace rtc;
using std::optional;
using std::shared_ptr;
using std::string;
using std::chrono::milliseconds;
namespace {

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -17,27 +17,36 @@
*/
#include "channel.hpp"
#include "globals.hpp"
#include "impl/channel.hpp"
namespace rtc {
Channel::~Channel() {
impl()->resetCallbacks();
}
Channel::Channel(impl_ptr<impl::Channel> impl) : CheshireCat<impl::Channel>(std::move(impl)) {}
size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
size_t Channel::bufferedAmount() const { return mBufferedAmount; }
size_t Channel::bufferedAmount() const { return impl()->bufferedAmount; }
size_t Channel::availableAmount() const { return 0; }
void Channel::onOpen(std::function<void()> callback) { impl()->openCallback = callback; }
void Channel::onOpen(std::function<void()> callback) { mOpenCallback = callback; }
void Channel::onClosed(std::function<void()> callback) { impl()->closedCallback = callback; }
void Channel::onClosed(std::function<void()> callback) { mClosedCallback = callback; }
void Channel::onError(std::function<void(string error)> callback) { mErrorCallback = callback; }
void Channel::onError(std::function<void(string error)> callback) {
impl()->errorCallback = callback;
}
void Channel::onMessage(std::function<void(message_variant data)> callback) {
mMessageCallback = callback;
impl()->messageCallback = callback;
// Pass pending messages
while (auto message = receive())
mMessageCallback(*message);
impl()->messageCallback(*message);
}
void Channel::onMessage(std::function<void(binary data)> binaryCallback,
@ -48,45 +57,25 @@ void Channel::onMessage(std::function<void(binary data)> binaryCallback,
}
void Channel::onBufferedAmountLow(std::function<void()> callback) {
mBufferedAmountLowCallback = callback;
impl()->bufferedAmountLowCallback = callback;
}
void Channel::setBufferedAmountLowThreshold(size_t amount) { mBufferedAmountLowThreshold = amount; }
void Channel::onAvailable(std::function<void()> callback) { mAvailableCallback = callback; }
void Channel::triggerOpen() { mOpenCallback(); }
void Channel::triggerClosed() { mClosedCallback(); }
void Channel::triggerError(string error) { mErrorCallback(error); }
void Channel::triggerAvailable(size_t count) {
if (count == 1)
mAvailableCallback();
while (mMessageCallback && count--) {
auto message = receive();
if (!message)
break;
mMessageCallback(*message);
}
void Channel::setBufferedAmountLowThreshold(size_t amount) {
impl()->bufferedAmountLowThreshold = amount;
}
void Channel::triggerBufferedAmount(size_t amount) {
size_t previous = mBufferedAmount.exchange(amount);
size_t threshold = mBufferedAmountLowThreshold.load();
if (previous > threshold && amount <= threshold)
mBufferedAmountLowCallback();
std::optional<message_variant> Channel::receive() {
return impl()->receive();
}
void Channel::resetCallbacks() {
mOpenCallback = nullptr;
mClosedCallback = nullptr;
mErrorCallback = nullptr;
mMessageCallback = nullptr;
mAvailableCallback = nullptr;
mBufferedAmountLowCallback = nullptr;
std::optional<message_variant> Channel::peek() {
return impl()->peek();
}
size_t Channel::availableAmount() const {
return impl()->availableAmount();
}
void Channel::onAvailable(std::function<void()> callback) { impl()->availableCallback = callback; }
} // namespace rtc

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -17,10 +17,12 @@
*/
#include "datachannel.hpp"
#include "include.hpp"
#include "logcounter.hpp"
#include "globals.hpp"
#include "common.hpp"
#include "peerconnection.hpp"
#include "sctptransport.hpp"
#include "impl/datachannel.hpp"
#include "impl/peerconnection.hpp"
#ifdef _WIN32
#include <winsock2.h>
@ -30,334 +32,36 @@
namespace rtc {
LogCounter COUNTER_USERNEG_OPEN_MESSAGE(
plog::warning, "Number of open messages for a user-negotiated DataChannel received");
using std::shared_ptr;
using std::weak_ptr;
using std::chrono::milliseconds;
// Messages for the DataChannel establishment protocol
// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09
enum MessageType : uint8_t {
MESSAGE_OPEN_REQUEST = 0x00,
MESSAGE_OPEN_RESPONSE = 0x01,
MESSAGE_ACK = 0x02,
MESSAGE_OPEN = 0x03,
MESSAGE_CLOSE = 0x04
};
enum ChannelType : uint8_t {
CHANNEL_RELIABLE = 0x00,
CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01,
CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02
};
#pragma pack(push, 1)
struct OpenMessage {
uint8_t type = MESSAGE_OPEN;
uint8_t channelType;
uint16_t priority;
uint32_t reliabilityParameter;
uint16_t labelLength;
uint16_t protocolLength;
// The following fields are:
// uint8_t[labelLength] label
// uint8_t[protocolLength] protocol
};
struct AckMessage {
uint8_t type = MESSAGE_ACK;
};
struct CloseMessage {
uint8_t type = MESSAGE_CLOSE;
};
#pragma pack(pop)
DataChannel::DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
string protocol, Reliability reliability)
: mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
mProtocol(std::move(protocol)),
mReliability(std::make_shared<Reliability>(std::move(reliability))),
mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
DataChannel::DataChannel(impl_ptr<impl::DataChannel> impl)
: CheshireCat<impl::DataChannel>(impl),
Channel(std::dynamic_pointer_cast<impl::Channel>(impl)) {}
DataChannel::~DataChannel() { close(); }
uint16_t DataChannel::stream() const { return mStream; }
void DataChannel::close() { return impl()->close(); }
uint16_t DataChannel::id() const { return mStream; }
uint16_t DataChannel::stream() const { return impl()->stream(); }
string DataChannel::label() const {
std::shared_lock lock(mMutex);
return mLabel;
uint16_t DataChannel::id() const { return impl()->stream(); }
string DataChannel::label() const { return impl()->label(); }
string DataChannel::protocol() const { return impl()->protocol(); }
Reliability DataChannel::reliability() const { return impl()->reliability(); }
bool DataChannel::isOpen(void) const { return impl()->isOpen(); }
bool DataChannel::isClosed(void) const { return impl()->isClosed(); }
size_t DataChannel::maxMessageSize() const { return impl()->maxMessageSize(); }
bool DataChannel::send(message_variant data) {
return impl()->outgoing(make_message(std::move(data)));
}
string DataChannel::protocol() const {
std::shared_lock lock(mMutex);
return mProtocol;
}
Reliability DataChannel::reliability() const {
std::shared_lock lock(mMutex);
return *mReliability;
}
void DataChannel::close() {
std::shared_ptr<SctpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mSctpTransport.lock();
}
mIsClosed = true;
if (mIsOpen.exchange(false) && transport)
transport->closeStream(mStream);
resetCallbacks();
}
void DataChannel::remoteClose() {
if (!mIsClosed.exchange(true))
triggerClosed();
mIsOpen = false;
}
bool DataChannel::send(message_variant data) { return outgoing(make_message(std::move(data))); }
bool DataChannel::send(const byte *data, size_t size) {
return outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
}
std::optional<message_variant> DataChannel::receive() {
while (auto next = mRecvQueue.tryPop()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
auto raw = reinterpret_cast<const uint8_t *>(message->data());
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
remoteClose();
}
return nullopt;
}
std::optional<message_variant> DataChannel::peek() {
while (auto next = mRecvQueue.peek()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
auto raw = reinterpret_cast<const uint8_t *>(message->data());
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
remoteClose();
mRecvQueue.tryPop();
}
return nullopt;
}
bool DataChannel::isOpen(void) const { return mIsOpen; }
bool DataChannel::isClosed(void) const { return mIsClosed; }
size_t DataChannel::maxMessageSize() const {
size_t remoteMax = DEFAULT_MAX_MESSAGE_SIZE;
if (auto pc = mPeerConnection.lock())
if (auto description = pc->remoteDescription())
if (auto *application = description->application())
if (auto maxMessageSize = application->maxMessageSize())
remoteMax = *maxMessageSize > 0 ? *maxMessageSize : LOCAL_MAX_MESSAGE_SIZE;
return std::min(remoteMax, LOCAL_MAX_MESSAGE_SIZE);
}
size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
void DataChannel::open(shared_ptr<SctpTransport> transport) {
{
std::unique_lock lock(mMutex);
mSctpTransport = transport;
}
if (!mIsOpen.exchange(true))
triggerOpen();
}
void DataChannel::processOpenMessage(message_ptr) {
PLOG_DEBUG << "Received an open message for a user-negotiated DataChannel, ignoring";
COUNTER_USERNEG_OPEN_MESSAGE++;
}
bool DataChannel::outgoing(message_ptr message) {
std::shared_ptr<SctpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mSctpTransport.lock();
if (!transport || mIsClosed)
throw std::runtime_error("DataChannel is closed");
if (message->size() > maxMessageSize())
throw std::runtime_error("Message size exceeds limit");
// Before the ACK has been received on a DataChannel, all messages must be sent ordered
message->reliability = mIsOpen ? mReliability : nullptr;
message->stream = mStream;
}
return transport->send(message);
}
void DataChannel::incoming(message_ptr message) {
if (!message)
return;
switch (message->type) {
case Message::Control: {
if (message->size() == 0)
break; // Ignore
auto raw = reinterpret_cast<const uint8_t *>(message->data());
switch (raw[0]) {
case MESSAGE_OPEN:
processOpenMessage(message);
break;
case MESSAGE_ACK:
if (!mIsOpen.exchange(true)) {
triggerOpen();
}
break;
case MESSAGE_CLOSE:
// The close message will be processed in-order in receive()
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
break;
default:
// Ignore
break;
}
break;
}
case Message::String:
case Message::Binary:
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
break;
default:
// Ignore
break;
}
}
NegotiatedDataChannel::NegotiatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream,
string label, string protocol, Reliability reliability)
: DataChannel(pc, stream, std::move(label), std::move(protocol), std::move(reliability)) {}
NegotiatedDataChannel::NegotiatedDataChannel(std::weak_ptr<PeerConnection> pc,
std::weak_ptr<SctpTransport> transport,
uint16_t stream)
: DataChannel(pc, stream, "", "", {}) {
mSctpTransport = transport;
}
NegotiatedDataChannel::~NegotiatedDataChannel() {}
void NegotiatedDataChannel::open(shared_ptr<SctpTransport> transport) {
std::unique_lock lock(mMutex);
mSctpTransport = transport;
uint8_t channelType;
uint32_t reliabilityParameter;
switch (mReliability->type) {
case Reliability::Type::Rexmit:
channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
reliabilityParameter = uint32_t(std::get<int>(mReliability->rexmit));
break;
case Reliability::Type::Timed:
channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
break;
default:
channelType = CHANNEL_RELIABLE;
reliabilityParameter = 0;
break;
}
if (mReliability->unordered)
channelType |= 0x80;
const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
binary buffer(len, byte(0));
auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
open.type = MESSAGE_OPEN;
open.channelType = channelType;
open.priority = htons(0);
open.reliabilityParameter = htonl(reliabilityParameter);
open.labelLength = htons(uint16_t(mLabel.size()));
open.protocolLength = htons(uint16_t(mProtocol.size()));
auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
std::copy(mLabel.begin(), mLabel.end(), end);
std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
lock.unlock();
transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
}
void NegotiatedDataChannel::processOpenMessage(message_ptr message) {
std::unique_lock lock(mMutex);
auto transport = mSctpTransport.lock();
if (!transport)
throw std::runtime_error("DataChannel has no transport");
if (message->size() < sizeof(OpenMessage))
throw std::invalid_argument("DataChannel open message too small");
OpenMessage open = *reinterpret_cast<const OpenMessage *>(message->data());
open.priority = ntohs(open.priority);
open.reliabilityParameter = ntohl(open.reliabilityParameter);
open.labelLength = ntohs(open.labelLength);
open.protocolLength = ntohs(open.protocolLength);
if (message->size() < sizeof(OpenMessage) + size_t(open.labelLength + open.protocolLength))
throw std::invalid_argument("DataChannel open message truncated");
auto end = reinterpret_cast<const char *>(message->data() + sizeof(OpenMessage));
mLabel.assign(end, open.labelLength);
mProtocol.assign(end + open.labelLength, open.protocolLength);
mReliability->unordered = (open.channelType & 0x80) != 0;
switch (open.channelType & 0x7F) {
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
mReliability->type = Reliability::Type::Rexmit;
mReliability->rexmit = int(open.reliabilityParameter);
break;
case CHANNEL_PARTIAL_RELIABLE_TIMED:
mReliability->type = Reliability::Type::Timed;
mReliability->rexmit = milliseconds(open.reliabilityParameter);
break;
default:
mReliability->type = Reliability::Type::Reliable;
mReliability->rexmit = int(0);
}
lock.unlock();
binary buffer(sizeof(AckMessage), byte(0));
auto &ack = *reinterpret_cast<AckMessage *>(buffer.data());
ack.type = MESSAGE_ACK;
transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
if (!mIsOpen.exchange(true))
triggerOpen();
return impl()->outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
}
} // namespace rtc

View File

@ -28,7 +28,6 @@
#include <sstream>
#include <unordered_map>
using std::shared_ptr;
using std::chrono::system_clock;
namespace {

41
src/globals.hpp Normal file
View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_GLOBALS_H
#define RTC_GLOBALS_H
#include "common.hpp"
namespace rtc {
const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length
const size_t MAX_NUMERICSERV_LEN = 6; // Max port string representation length
const uint16_t DEFAULT_SCTP_PORT = 5000; // SCTP port to use by default
const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536; // Remote max message size if not specified in SDP
const size_t LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Local max message size
const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
const int THREADPOOL_SIZE = 4; // Number of threads in the global thread pool (>= 2)
const size_t DEFAULT_IPV4_MTU = 1200; // IPv4 safe MTU value recommended by RFC 8261
} // namespace rtc
#endif

View File

@ -22,9 +22,6 @@
namespace rtc {
using std::make_shared;
using std::shared_ptr;
typedef enum {
NUSM_noMatch,
NUSM_firstZero,
@ -74,7 +71,7 @@ NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match
}
shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
auto nalus = make_shared<NalUnits>();
auto nalus = std::make_shared<NalUnits>();
if (separator == Separator::Length) {
unsigned long long index = 0;
while (index < message->size()) {

View File

@ -20,7 +20,7 @@
#include "base64.hpp"
namespace rtc {
namespace rtc::impl {
using std::to_integer;
@ -59,6 +59,6 @@ string to_base64(const binary &data) {
return out;
}
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -16,14 +16,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_BASE64_H
#define RTC_BASE64_H
#ifndef RTC_IMPL_BASE64_H
#define RTC_IMPL_BASE64_H
#if RTC_ENABLE_WEBSOCKET
#include "include.hpp"
#include "common.hpp"
namespace rtc {
namespace rtc::impl {
string to_base64(const binary &data);

View File

@ -26,14 +26,10 @@
#include <sstream>
#include <unordered_map>
using std::shared_ptr;
using std::string;
using std::unique_ptr;
namespace rtc::impl {
#if USE_GNUTLS
namespace rtc {
Certificate::Certificate(string crt_pem, string key_pem)
: mCredentials(gnutls::new_credentials(), gnutls::free_credentials) {
@ -129,12 +125,8 @@ certificate_ptr make_certificate_impl(string commonName) {
} // namespace
} // namespace rtc
#else // USE_GNUTLS==0
namespace rtc {
Certificate::Certificate(string crt_pem, string key_pem) {
BIO *bio = BIO_new(BIO_s_mem());
BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
@ -230,14 +222,10 @@ certificate_ptr make_certificate_impl(string commonName) {
} // namespace
} // namespace rtc
#endif
// Common for GnuTLS and OpenSSL
namespace rtc {
namespace {
static std::unordered_map<string, future_certificate_ptr> CertificateCache;
@ -262,4 +250,4 @@ void CleanupCertificateCache() {
CertificateCache.clear();
}
} // namespace rtc
} // namespace rtc::impl

View File

@ -16,16 +16,16 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_CERTIFICATE_H
#define RTC_CERTIFICATE_H
#ifndef RTC_IMPL_CERTIFICATE_H
#define RTC_IMPL_CERTIFICATE_H
#include "include.hpp"
#include "common.hpp"
#include "tls.hpp"
#include <future>
#include <tuple>
namespace rtc {
namespace rtc::impl {
class Certificate {
public:
@ -65,6 +65,6 @@ future_certificate_ptr make_certificate(string commonName = "libdatachannel"); /
void CleanupCertificateCache();
} // namespace rtc
} // namespace rtc::impl
#endif

57
src/impl/channel.cpp Normal file
View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "channel.hpp"
namespace rtc::impl {
void Channel::triggerOpen() { openCallback(); }
void Channel::triggerClosed() { closedCallback(); }
void Channel::triggerError(string error) { errorCallback(error); }
void Channel::triggerAvailable(size_t count) {
if (count == 1)
availableCallback();
while (messageCallback && count--) {
auto message = receive();
if (!message)
break;
messageCallback(*message);
}
}
void Channel::triggerBufferedAmount(size_t amount) {
size_t previous = bufferedAmount.exchange(amount);
size_t threshold = bufferedAmountLowThreshold.load();
if (previous > threshold && amount <= threshold)
bufferedAmountLowCallback();
}
void Channel::resetCallbacks() {
openCallback = nullptr;
closedCallback = nullptr;
errorCallback = nullptr;
messageCallback = nullptr;
availableCallback = nullptr;
bufferedAmountLowCallback = nullptr;
}
} // namespace rtc::impl

57
src/impl/channel.hpp Normal file
View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_IMPL_CHANNEL_H
#define RTC_IMPL_CHANNEL_H
#include "common.hpp"
#include "message.hpp"
#include <atomic>
#include <functional>
#include <variant>
namespace rtc::impl {
struct Channel {
virtual std::optional<message_variant> receive() = 0;
virtual std::optional<message_variant> peek() = 0;
virtual size_t availableAmount() const = 0;
virtual void triggerOpen();
virtual void triggerClosed();
virtual void triggerError(string error);
virtual void triggerAvailable(size_t count);
virtual void triggerBufferedAmount(size_t amount);
virtual void resetCallbacks();
synchronized_callback<> openCallback;
synchronized_callback<> closedCallback;
synchronized_callback<string> errorCallback;
synchronized_callback<message_variant> messageCallback;
synchronized_callback<> availableCallback;
synchronized_callback<> bufferedAmountLowCallback;
std::atomic<size_t> bufferedAmount = 0;
std::atomic<size_t> bufferedAmountLowThreshold = 0;
};
} // namespace rtc::impl
#endif

366
src/impl/datachannel.cpp Normal file
View File

@ -0,0 +1,366 @@
/**
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "datachannel.hpp"
#include "globals.hpp"
#include "common.hpp"
#include "logcounter.hpp"
#include "peerconnection.hpp"
#include "sctptransport.hpp"
#include "rtc/datachannel.hpp"
#include "rtc/track.hpp"
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
using std::chrono::milliseconds;
namespace rtc::impl {
// Messages for the DataChannel establishment protocol
// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09
enum MessageType : uint8_t {
MESSAGE_OPEN_REQUEST = 0x00,
MESSAGE_OPEN_RESPONSE = 0x01,
MESSAGE_ACK = 0x02,
MESSAGE_OPEN = 0x03,
MESSAGE_CLOSE = 0x04
};
enum ChannelType : uint8_t {
CHANNEL_RELIABLE = 0x00,
CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01,
CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02
};
#pragma pack(push, 1)
struct OpenMessage {
uint8_t type = MESSAGE_OPEN;
uint8_t channelType;
uint16_t priority;
uint32_t reliabilityParameter;
uint16_t labelLength;
uint16_t protocolLength;
// The following fields are:
// uint8_t[labelLength] label
// uint8_t[protocolLength] protocol
};
struct AckMessage {
uint8_t type = MESSAGE_ACK;
};
struct CloseMessage {
uint8_t type = MESSAGE_CLOSE;
};
#pragma pack(pop)
LogCounter COUNTER_USERNEG_OPEN_MESSAGE(
plog::warning, "Number of open messages for a user-negotiated DataChannel received");
DataChannel::DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
string protocol, Reliability reliability)
: mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
mProtocol(std::move(protocol)),
mReliability(std::make_shared<Reliability>(std::move(reliability))),
mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
DataChannel::~DataChannel() { close(); }
void DataChannel::close() {
std::shared_ptr<SctpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mSctpTransport.lock();
}
mIsClosed = true;
if (mIsOpen.exchange(false) && transport)
transport->closeStream(mStream);
resetCallbacks();
}
void DataChannel::remoteClose() {
if (!mIsClosed.exchange(true))
triggerClosed();
mIsOpen = false;
}
std::optional<message_variant> DataChannel::receive() {
while (auto next = mRecvQueue.tryPop()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
auto raw = reinterpret_cast<const uint8_t *>(message->data());
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
remoteClose();
}
return nullopt;
}
std::optional<message_variant> DataChannel::peek() {
while (auto next = mRecvQueue.peek()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
auto raw = reinterpret_cast<const uint8_t *>(message->data());
if (!message->empty() && raw[0] == MESSAGE_CLOSE)
remoteClose();
mRecvQueue.tryPop();
}
return nullopt;
}
size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
uint16_t DataChannel::stream() const {
std::shared_lock lock(mMutex);
return mStream;
}
string DataChannel::label() const {
std::shared_lock lock(mMutex);
return mLabel;
}
string DataChannel::protocol() const {
std::shared_lock lock(mMutex);
return mProtocol;
}
Reliability DataChannel::reliability() const {
std::shared_lock lock(mMutex);
return *mReliability;
}
bool DataChannel::isOpen(void) const { return mIsOpen; }
bool DataChannel::isClosed(void) const { return mIsClosed; }
size_t DataChannel::maxMessageSize() const {
size_t remoteMax = DEFAULT_MAX_MESSAGE_SIZE;
if (auto pc = mPeerConnection.lock())
if (auto description = pc->remoteDescription())
if (auto *application = description->application())
if (auto maxMessageSize = application->maxMessageSize())
remoteMax = *maxMessageSize > 0 ? *maxMessageSize : LOCAL_MAX_MESSAGE_SIZE;
return std::min(remoteMax, LOCAL_MAX_MESSAGE_SIZE);
}
void DataChannel::shiftStream() {
if (mStream % 2 == 1)
mStream -= 1;
}
void DataChannel::open(shared_ptr<SctpTransport> transport) {
{
std::unique_lock lock(mMutex);
mSctpTransport = transport;
}
if (!mIsOpen.exchange(true))
triggerOpen();
}
void DataChannel::processOpenMessage(message_ptr) {
PLOG_DEBUG << "Received an open message for a user-negotiated DataChannel, ignoring";
COUNTER_USERNEG_OPEN_MESSAGE++;
}
bool DataChannel::outgoing(message_ptr message) {
std::shared_ptr<SctpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mSctpTransport.lock();
if (!transport || mIsClosed)
throw std::runtime_error("DataChannel is closed");
if (message->size() > maxMessageSize())
throw std::runtime_error("Message size exceeds limit");
// Before the ACK has been received on a DataChannel, all messages must be sent ordered
message->reliability = mIsOpen ? mReliability : nullptr;
message->stream = mStream;
}
return transport->send(message);
}
void DataChannel::incoming(message_ptr message) {
if (!message)
return;
switch (message->type) {
case Message::Control: {
if (message->size() == 0)
break; // Ignore
auto raw = reinterpret_cast<const uint8_t *>(message->data());
switch (raw[0]) {
case MESSAGE_OPEN:
processOpenMessage(message);
break;
case MESSAGE_ACK:
if (!mIsOpen.exchange(true)) {
triggerOpen();
}
break;
case MESSAGE_CLOSE:
// The close message will be processed in-order in receive()
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
break;
default:
// Ignore
break;
}
break;
}
case Message::String:
case Message::Binary:
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
break;
default:
// Ignore
break;
}
}
NegotiatedDataChannel::NegotiatedDataChannel(std::weak_ptr<impl::PeerConnection> pc,
uint16_t stream, string label, string protocol,
Reliability reliability)
: DataChannel(pc, stream, std::move(label), std::move(protocol), std::move(reliability)) {}
NegotiatedDataChannel::NegotiatedDataChannel(std::weak_ptr<impl::PeerConnection> pc,
std::weak_ptr<impl::SctpTransport> transport,
uint16_t stream)
: DataChannel(pc, stream, "", "", {}) {
mSctpTransport = transport;
}
NegotiatedDataChannel::~NegotiatedDataChannel() {}
void NegotiatedDataChannel::open(shared_ptr<impl::SctpTransport> transport) {
std::unique_lock lock(mMutex);
mSctpTransport = transport;
uint8_t channelType;
uint32_t reliabilityParameter;
switch (mReliability->type) {
case Reliability::Type::Rexmit:
channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
reliabilityParameter = uint32_t(std::get<int>(mReliability->rexmit));
break;
case Reliability::Type::Timed:
channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
break;
default:
channelType = CHANNEL_RELIABLE;
reliabilityParameter = 0;
break;
}
if (mReliability->unordered)
channelType |= 0x80;
const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
binary buffer(len, byte(0));
auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
open.type = MESSAGE_OPEN;
open.channelType = channelType;
open.priority = htons(0);
open.reliabilityParameter = htonl(reliabilityParameter);
open.labelLength = htons(uint16_t(mLabel.size()));
open.protocolLength = htons(uint16_t(mProtocol.size()));
auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
std::copy(mLabel.begin(), mLabel.end(), end);
std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
lock.unlock();
transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
}
void NegotiatedDataChannel::processOpenMessage(message_ptr message) {
std::unique_lock lock(mMutex);
auto transport = mSctpTransport.lock();
if (!transport)
throw std::runtime_error("DataChannel has no transport");
if (message->size() < sizeof(OpenMessage))
throw std::invalid_argument("DataChannel open message too small");
OpenMessage open = *reinterpret_cast<const OpenMessage *>(message->data());
open.priority = ntohs(open.priority);
open.reliabilityParameter = ntohl(open.reliabilityParameter);
open.labelLength = ntohs(open.labelLength);
open.protocolLength = ntohs(open.protocolLength);
if (message->size() < sizeof(OpenMessage) + size_t(open.labelLength + open.protocolLength))
throw std::invalid_argument("DataChannel open message truncated");
auto end = reinterpret_cast<const char *>(message->data() + sizeof(OpenMessage));
mLabel.assign(end, open.labelLength);
mProtocol.assign(end + open.labelLength, open.protocolLength);
mReliability->unordered = (open.channelType & 0x80) != 0;
switch (open.channelType & 0x7F) {
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
mReliability->type = Reliability::Type::Rexmit;
mReliability->rexmit = int(open.reliabilityParameter);
break;
case CHANNEL_PARTIAL_RELIABLE_TIMED:
mReliability->type = Reliability::Type::Timed;
mReliability->rexmit = milliseconds(open.reliabilityParameter);
break;
default:
mReliability->type = Reliability::Type::Reliable;
mReliability->rexmit = int(0);
}
lock.unlock();
binary buffer(sizeof(AckMessage), byte(0));
auto &ack = *reinterpret_cast<AckMessage *>(buffer.data());
ack.type = MESSAGE_ACK;
transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
if (!mIsOpen.exchange(true))
triggerOpen();
}
} // namespace rtc::impl

94
src/impl/datachannel.hpp Normal file
View File

@ -0,0 +1,94 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_IMPL_DATA_CHANNEL_H
#define RTC_IMPL_DATA_CHANNEL_H
#include "channel.hpp"
#include "common.hpp"
#include "message.hpp"
#include "peerconnection.hpp"
#include "queue.hpp"
#include "reliability.hpp"
#include "sctptransport.hpp"
#include <atomic>
namespace rtc::impl {
struct PeerConnection;
struct DataChannel : Channel, std::enable_shared_from_this<DataChannel> {
DataChannel(weak_ptr<impl::PeerConnection> pc, uint16_t stream, string label, string protocol,
Reliability reliability);
~DataChannel();
void close();
void remoteClose();
bool outgoing(message_ptr message);
void incoming(message_ptr message);
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
size_t availableAmount() const override;
uint16_t stream() const;
string label() const;
string protocol() const;
Reliability reliability() const;
bool isOpen(void) const;
bool isClosed(void) const;
size_t maxMessageSize() const;
void shiftStream();
virtual void open(shared_ptr<SctpTransport> transport);
virtual void processOpenMessage(message_ptr);
protected:
const weak_ptr<impl::PeerConnection> mPeerConnection;
weak_ptr<SctpTransport> mSctpTransport;
uint16_t mStream;
string mLabel;
string mProtocol;
shared_ptr<Reliability> mReliability;
mutable std::shared_mutex mMutex;
Queue<message_ptr> mRecvQueue;
std::atomic<bool> mIsOpen = false;
std::atomic<bool> mIsClosed = false;
};
struct NegotiatedDataChannel final : public DataChannel {
NegotiatedDataChannel(weak_ptr<impl::PeerConnection> pc, uint16_t stream, string label,
string protocol, Reliability reliability);
NegotiatedDataChannel(weak_ptr<impl::PeerConnection> pc,
weak_ptr<impl::SctpTransport> transport, uint16_t stream);
~NegotiatedDataChannel();
void open(impl_ptr<impl::SctpTransport> transport) override;
void processOpenMessage(message_ptr message) override;
};
} // namespace rtc::impl
#endif

View File

@ -19,17 +19,17 @@
#include "dtlssrtptransport.hpp"
#include "logcounter.hpp"
#include "tls.hpp"
#include "rtp.hpp"
#if RTC_ENABLE_MEDIA
#include <cstring>
#include <exception>
using std::shared_ptr;
using std::to_integer;
using std::to_string;
namespace rtc {
namespace rtc::impl {
static LogCounter COUNTER_MEDIA_TRUNCATED(plog::warning,
"Number of truncated SRT(C)P packets received");
@ -48,7 +48,7 @@ static LogCounter COUNTER_SRTP_REPLAY(plog::warning, "Number of SRTP replay pack
static LogCounter
COUNTER_SRTP_AUTH_FAIL(plog::warning,
"Number of SRTP packets received that failed authentication checks");
static rtc::LogCounter
static LogCounter
COUNTER_SRTP_FAIL(plog::warning,
"Number of SRTP packets received that had an unknown libSRTP failure");

View File

@ -16,11 +16,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_DTLS_SRTP_TRANSPORT_H
#define RTC_DTLS_SRTP_TRANSPORT_H
#ifndef RTC_IMPL_DTLS_SRTP_TRANSPORT_H
#define RTC_IMPL_DTLS_SRTP_TRANSPORT_H
#include "dtlstransport.hpp"
#include "include.hpp"
#include "common.hpp"
#if RTC_ENABLE_MEDIA
@ -32,7 +32,7 @@
#include <atomic>
namespace rtc {
namespace rtc::impl {
class DtlsSrtpTransport final : public DtlsTransport {
public:

View File

@ -17,6 +17,7 @@
*/
#include "dtlstransport.hpp"
#include "globals.hpp"
#include "icetransport.hpp"
#include <chrono>
@ -34,12 +35,7 @@
using namespace std::chrono;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::weak_ptr;
namespace rtc {
namespace rtc::impl {
#if USE_GNUTLS
@ -597,4 +593,4 @@ long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /
#endif
} // namespace rtc
} // namespace rtc::impl

View File

@ -16,12 +16,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_DTLS_TRANSPORT_H
#define RTC_DTLS_TRANSPORT_H
#ifndef RTC_IMPL_DTLS_TRANSPORT_H
#define RTC_IMPL_DTLS_TRANSPORT_H
#include "certificate.hpp"
#include "include.hpp"
#include "peerconnection.hpp"
#include "common.hpp"
#include "queue.hpp"
#include "tls.hpp"
#include "transport.hpp"
@ -32,7 +31,7 @@
#include <mutex>
#include <thread>
namespace rtc {
namespace rtc::impl {
class IceTransport;

View File

@ -18,6 +18,7 @@
#include "icetransport.hpp"
#include "configuration.hpp"
#include "globals.hpp"
#include "transport.hpp"
#include <iostream>
@ -37,17 +38,14 @@
#include <sys/types.h>
using namespace std::chrono_literals;
using std::shared_ptr;
using std::weak_ptr;
using std::chrono::system_clock;
namespace rtc::impl {
#if !USE_NICE
#define MAX_TURN_SERVERS_COUNT 2
namespace rtc {
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
state_callback stateChangeCallback,
gathering_state_callback gatheringStateChangeCallback)
@ -282,7 +280,7 @@ void IceTransport::processCandidate(const string &candidate) {
void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
try {
iceTransport->processStateChange(static_cast<unsigned int>(state));
} catch (const std::exception &e) {
@ -291,7 +289,7 @@ void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, voi
}
void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
try {
iceTransport->processCandidate(sdp);
} catch (const std::exception &e) {
@ -300,7 +298,7 @@ void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *use
}
void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
try {
iceTransport->processGatheringDone();
} catch (const std::exception &e) {
@ -309,7 +307,7 @@ void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
}
void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
try {
PLOG_VERBOSE << "Incoming size=" << size;
auto b = reinterpret_cast<const byte *>(data);
@ -341,12 +339,8 @@ void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
PLOG(severity) << "juice: " << message;
}
} // namespace rtc
#else // USE_NICE == 1
namespace rtc {
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
state_callback stateChangeCallback,
gathering_state_callback gatheringStateChangeCallback)
@ -717,7 +711,7 @@ string IceTransport::AddressToString(const NiceAddress &addr) {
void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);
try {
iceTransport->processCandidate(cand);
@ -729,7 +723,7 @@ void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId*/,
gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
try {
iceTransport->processGatheringDone();
} catch (const std::exception &e) {
@ -739,7 +733,7 @@ void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId
void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/,
guint /*componentId*/, guint state, gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
try {
iceTransport->processStateChange(state);
} catch (const std::exception &e) {
@ -749,7 +743,7 @@ void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/
void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint /*componentId*/,
guint len, gchar *buf, gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
try {
PLOG_VERBOSE << "Incoming size=" << len;
auto b = reinterpret_cast<byte *>(buf);
@ -760,7 +754,7 @@ void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint
}
gboolean IceTransport::TimeoutCallback(gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
try {
iceTransport->processTimeout();
} catch (const std::exception &e) {
@ -811,6 +805,6 @@ bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote)
return true;
}
} // namespace rtc
#endif
} // namespace rtc::impl

View File

@ -16,13 +16,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_ICE_TRANSPORT_H
#define RTC_ICE_TRANSPORT_H
#ifndef RTC_IMPL_ICE_TRANSPORT_H
#define RTC_IMPL_ICE_TRANSPORT_H
#include "candidate.hpp"
#include "configuration.hpp"
#include "description.hpp"
#include "include.hpp"
#include "common.hpp"
#include "peerconnection.hpp"
#include "transport.hpp"
@ -37,7 +37,7 @@
#include <mutex>
#include <thread>
namespace rtc {
namespace rtc::impl {
class IceTransport : public Transport {
public:

View File

@ -18,7 +18,7 @@
#include "logcounter.hpp"
namespace rtc {
namespace rtc::impl {
LogCounter::LogCounter(plog::Severity severity, const std::string &text,
std::chrono::seconds duration) {

View File

@ -19,13 +19,13 @@
#ifndef RTC_SERVER_LOGCOUNTER_HPP
#define RTC_SERVER_LOGCOUNTER_HPP
#include "include.hpp"
#include "common.hpp"
#include "threadpool.hpp"
#include <atomic>
#include <chrono>
namespace rtc {
namespace rtc::impl {
class LogCounter {
private:

1011
src/impl/peerconnection.cpp Normal file

File diff suppressed because it is too large Load Diff

129
src/impl/peerconnection.hpp Normal file
View File

@ -0,0 +1,129 @@
/**
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_IMPL_PEER_CONNECTION_H
#define RTC_IMPL_PEER_CONNECTION_H
#include "datachannel.hpp"
#include "dtlstransport.hpp"
#include "icetransport.hpp"
#include "common.hpp"
#include "sctptransport.hpp"
#include "track.hpp"
#include "rtc/peerconnection.hpp"
namespace rtc::impl {
struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
using State = rtc::PeerConnection::State;
using GatheringState = rtc::PeerConnection::GatheringState;
using SignalingState = rtc::PeerConnection::SignalingState;
PeerConnection(Configuration config_);
~PeerConnection();
void close();
std::optional<Description> localDescription() const;
std::optional<Description> remoteDescription() const;
std::shared_ptr<IceTransport> initIceTransport();
std::shared_ptr<DtlsTransport> initDtlsTransport();
std::shared_ptr<SctpTransport> initSctpTransport();
std::shared_ptr<IceTransport> getIceTransport() const;
std::shared_ptr<DtlsTransport> getDtlsTransport() const;
std::shared_ptr<SctpTransport> getSctpTransport() const;
void closeTransports();
void endLocalCandidates();
void rollbackLocalDescription();
bool checkFingerprint(const std::string &fingerprint) const;
void forwardMessage(message_ptr message);
void forwardMedia(message_ptr message);
void forwardBufferedAmount(uint16_t stream, size_t amount);
std::optional<string> getMidFromSsrc(uint32_t ssrc);
shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
DataChannelInit init);
shared_ptr<DataChannel> findDataChannel(uint16_t stream);
void shiftDataChannels();
void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
void openDataChannels();
void closeDataChannels();
void remoteCloseDataChannels();
shared_ptr<Track> emplaceTrack(Description::Media description);
void incomingTrack(Description::Media description);
void openTracks();
void validateRemoteDescription(const Description &description);
void processLocalDescription(Description description);
void processLocalCandidate(Candidate candidate);
void processRemoteDescription(Description description);
void processRemoteCandidate(Candidate candidate);
string localBundleMid() const;
void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
void triggerTrack(std::shared_ptr<Track> track);
bool changeState(State newState);
bool changeGatheringState(GatheringState newState);
bool changeSignalingState(SignalingState newState);
void resetCallbacks();
void outgoingMedia(message_ptr message);
const Configuration config;
std::atomic<State> state = State::New;
std::atomic<GatheringState> gatheringState = GatheringState::New;
std::atomic<SignalingState> signalingState = SignalingState::Stable;
std::atomic<bool> negotiationNeeded = false;
synchronized_callback<std::shared_ptr<rtc::DataChannel>> dataChannelCallback;
synchronized_callback<Description> localDescriptionCallback;
synchronized_callback<Candidate> localCandidateCallback;
synchronized_callback<State> stateChangeCallback;
synchronized_callback<GatheringState> gatheringStateChangeCallback;
synchronized_callback<SignalingState> signalingStateChangeCallback;
synchronized_callback<std::shared_ptr<rtc::Track>> trackCallback;
private:
const init_token mInitToken = Init::Token();
const future_certificate_ptr mCertificate;
const std::unique_ptr<Processor> mProcessor;
std::optional<Description> mLocalDescription, mRemoteDescription;
std::optional<Description> mCurrentLocalDescription;
mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
std::shared_ptr<IceTransport> mIceTransport;
std::shared_ptr<DtlsTransport> mDtlsTransport;
std::shared_ptr<SctpTransport> mSctpTransport;
std::unordered_map<uint16_t, std::weak_ptr<DataChannel>> mDataChannels; // by stream ID
std::unordered_map<string, std::weak_ptr<Track>> mTracks; // by mid
std::vector<std::weak_ptr<Track>> mTrackLines; // by SDP order
std::shared_mutex mDataChannelsMutex, mTracksMutex;
std::unordered_map<uint32_t, string> mMidFromSsrc; // cache
};
} // namespace rtc::impl
#endif

View File

@ -18,7 +18,7 @@
#include "processor.hpp"
namespace rtc {
namespace rtc::impl {
Processor::Processor(size_t limit) : mTasks(limit) {}
@ -40,4 +40,4 @@ void Processor::schedule() {
}
}
} // namespace rtc
} // namespace rtc::impl

View File

@ -16,10 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_PROCESSOR_H
#define RTC_PROCESSOR_H
#ifndef RTC_IMPL_PROCESSOR_H
#define RTC_IMPL_PROCESSOR_H
#include "include.hpp"
#include "common.hpp"
#include "init.hpp"
#include "queue.hpp"
#include "threadpool.hpp"
@ -30,7 +30,7 @@
#include <mutex>
#include <queue>
namespace rtc {
namespace rtc::impl {
// Processed tasks in order by delegating them to the thread pool
class Processor final {
@ -76,6 +76,6 @@ template <class F, class... Args> void Processor::enqueue(F &&f, Args &&...args)
}
}
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -16,10 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_QUEUE_H
#define RTC_QUEUE_H
#ifndef RTC_IMPL_QUEUE_H
#define RTC_IMPL_QUEUE_H
#include "include.hpp"
#include "common.hpp"
#include <atomic>
#include <chrono>
@ -28,7 +28,7 @@
#include <optional>
#include <queue>
namespace rtc {
namespace rtc::impl {
template <typename T> class Queue {
public:
@ -167,6 +167,6 @@ template <typename T> std::optional<T> Queue<T>::popImpl() {
return element;
}
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -18,6 +18,7 @@
#include "sctptransport.hpp"
#include "dtlstransport.hpp"
#include "globals.hpp"
#include "logcounter.hpp"
#include <chrono>
@ -54,7 +55,7 @@
using namespace std::chrono_literals;
using namespace std::chrono;
namespace rtc {
namespace rtc::impl {
static LogCounter COUNTER_UNKNOWN_PPID(plog::warning,
"Number of SCTP packets received with an unknown PPID");
@ -808,4 +809,4 @@ int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos,
return transport->handleWrite(static_cast<byte *>(data), len, tos, set_df);
}
} // namespace rtc
} // namespace rtc::impl

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 Paul-Louis Ageneau
* Copyright (c) 2019-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,11 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_SCTP_TRANSPORT_H
#define RTC_SCTP_TRANSPORT_H
#ifndef RTC_IMPL_SCTP_TRANSPORT_H
#define RTC_IMPL_SCTP_TRANSPORT_H
#include "include.hpp"
#include "peerconnection.hpp"
#include "common.hpp"
#include "processor.hpp"
#include "queue.hpp"
#include "transport.hpp"
@ -34,7 +33,7 @@
#include "usrsctp.h"
namespace rtc {
namespace rtc::impl {
class SctpTransport final : public Transport {
public:
@ -120,6 +119,6 @@ private:
static std::shared_mutex InstancesMutex;
};
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -17,6 +17,7 @@
*/
#include "tcptransport.hpp"
#include "globals.hpp"
#if RTC_ENABLE_WEBSOCKET
@ -27,7 +28,7 @@
#include <unistd.h>
#endif
namespace rtc {
namespace rtc::impl {
using std::to_string;
@ -398,6 +399,6 @@ int TcpTransport::prepareSelect(fd_set &readfds, fd_set &writefds) {
void TcpTransport::interruptSelect() { mInterrupter.interrupt(); }
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -16,10 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_TCP_TRANSPORT_H
#define RTC_TCP_TRANSPORT_H
#ifndef RTC_IMPL_TCP_TRANSPORT_H
#define RTC_IMPL_TCP_TRANSPORT_H
#include "include.hpp"
#include "common.hpp"
#include "queue.hpp"
#include "transport.hpp"
@ -31,7 +31,7 @@
// Use the socket defines from libjuice
#include "../deps/libjuice/src/socket.h"
namespace rtc {
namespace rtc::impl {
// Utility class to interrupt select()
class SelectInterrupter {
@ -85,7 +85,7 @@ private:
Queue<message_ptr> mSendQueue;
};
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -22,11 +22,11 @@
namespace {
void joinThreadPoolInstance() { rtc::ThreadPool::Instance().join(); }
void joinThreadPoolInstance() { rtc::impl::ThreadPool::Instance().join(); }
} // namespace
namespace rtc {
namespace rtc::impl {
ThreadPool &ThreadPool::Instance() {
static ThreadPool *instance = new ThreadPool;
@ -103,4 +103,4 @@ std::function<void()> ThreadPool::dequeue() {
return nullptr;
}
} // namespace rtc
} // namespace rtc::impl

View File

@ -16,10 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_THREADPOOL_H
#define RTC_THREADPOOL_H
#ifndef RTC_IMPL_THREADPOOL_H
#define RTC_IMPL_THREADPOOL_H
#include "include.hpp"
#include "common.hpp"
#include "init.hpp"
#include <chrono>
@ -34,7 +34,7 @@
#include <thread>
#include <vector>
namespace rtc {
namespace rtc::impl {
template <class F, class... Args>
using invoke_future_t = std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>;
@ -119,6 +119,6 @@ auto ThreadPool::schedule(clock::time_point time, F &&f, Args &&...args)
return result;
}
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -19,7 +19,7 @@
#ifndef RTC_TLS_H
#define RTC_TLS_H
#include "include.hpp"
#include "common.hpp"
#if USE_GNUTLS

View File

@ -28,12 +28,7 @@
using namespace std::chrono;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::weak_ptr;
namespace rtc {
namespace rtc::impl {
#if USE_GNUTLS
@ -443,6 +438,6 @@ void TlsTransport::InfoCallback(const SSL *ssl, int where, int ret) {
#endif
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -16,10 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_TLS_TRANSPORT_H
#define RTC_TLS_TRANSPORT_H
#ifndef RTC_IMPL_TLS_TRANSPORT_H
#define RTC_IMPL_TLS_TRANSPORT_H
#include "include.hpp"
#include "common.hpp"
#include "queue.hpp"
#include "tls.hpp"
#include "transport.hpp"
@ -28,7 +28,7 @@
#include <thread>
namespace rtc {
namespace rtc::impl {
class TcpTransport;
@ -76,7 +76,7 @@ protected:
#endif
};
} // namespace rtc
} // namespace rtc::impl
#endif

182
src/impl/track.cpp Normal file
View File

@ -0,0 +1,182 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "track.hpp"
#include "globals.hpp"
#include "logcounter.hpp"
namespace rtc::impl {
static LogCounter COUNTER_MEDIA_BAD_DIRECTION(plog::warning,
"Number of media packets sent in invalid directions");
static LogCounter COUNTER_QUEUE_FULL(plog::warning,
"Number of media packets dropped due to a full queue");
Track::Track(Description::Media description)
: mMediaDescription(std::move(description)), mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
string Track::mid() const {
std::shared_lock lock(mMutex);
return mMediaDescription.mid();
}
Description::Direction Track::direction() const {
std::shared_lock lock(mMutex);
return mMediaDescription.direction();
}
Description::Media Track::description() const {
std::shared_lock lock(mMutex);
return mMediaDescription;
}
void Track::setDescription(Description::Media description) {
std::unique_lock lock(mMutex);
if (description.mid() != mMediaDescription.mid())
throw std::logic_error("Media description mid does not match track mid");
mMediaDescription = std::move(description);
}
void Track::close() {
mIsClosed = true;
setRtcpHandler(nullptr);
resetCallbacks();
}
std::optional<message_variant> Track::receive() {
if (auto next = mRecvQueue.tryPop())
return to_variant(std::move(**next));
return nullopt;
}
std::optional<message_variant> Track::peek() {
if (auto next = mRecvQueue.peek())
return to_variant(std::move(**next));
return nullopt;
}
bool Track::isOpen(void) const {
#if RTC_ENABLE_MEDIA
std::shared_lock lock(mMutex);
return !mIsClosed && mDtlsSrtpTransport.lock();
#else
return !mIsClosed;
#endif
}
bool Track::isClosed(void) const { return mIsClosed; }
size_t Track::availableAmount() const { return mRecvQueue.amount(); }
#if RTC_ENABLE_MEDIA
void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
{
std::lock_guard lock(mMutex);
mDtlsSrtpTransport = transport;
}
triggerOpen();
}
#endif
void Track::incoming(message_ptr message) {
if (!message)
return;
// TODO
auto dir = direction();
if ((dir == Description::Direction::SendOnly || dir == Description::Direction::Inactive) &&
message->type != Message::Control) {
COUNTER_MEDIA_BAD_DIRECTION++;
return;
}
if (auto handler = getRtcpHandler()) {
message = handler->incoming(message);
if (!message)
return;
}
// Tail drop if queue is full
if (mRecvQueue.full()) {
COUNTER_QUEUE_FULL++;
return;
}
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
}
bool Track::outgoing([[maybe_unused]] message_ptr message) {
if (mIsClosed)
throw std::runtime_error("Track is closed");
auto dir = direction();
if ((dir == Description::Direction::RecvOnly || dir == Description::Direction::Inactive)) {
COUNTER_MEDIA_BAD_DIRECTION++;
return false;
}
if (auto handler = getRtcpHandler()) {
message = handler->outgoing(message);
if (!message)
return false;
}
#if RTC_ENABLE_MEDIA
std::shared_ptr<DtlsSrtpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mDtlsSrtpTransport.lock();
if (!transport)
throw std::runtime_error("Track is closed");
// Set recommended medium-priority DSCP value
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
if (mMediaDescription.type() == "audio")
message->dscp = 46; // EF: Expedited Forwarding
else
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
}
return transport->sendMedia(message);
#else
PLOG_WARNING << "Ignoring track send (not compiled with media support)";
return false;
#endif
}
void Track::setRtcpHandler(std::shared_ptr<MediaHandler> handler) {
{
std::unique_lock lock(mMutex);
mRtcpHandler = handler;
}
handler->onOutgoing(std::bind(&Track::outgoing, this, std::placeholders::_1));
}
std::shared_ptr<MediaHandler> Track::getRtcpHandler() {
std::shared_lock lock(mMutex);
return mRtcpHandler;
}
} // namespace rtc::impl

83
src/impl/track.hpp Normal file
View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_IMPL_TRACK_H
#define RTC_IMPL_TRACK_H
#include "channel.hpp"
#include "common.hpp"
#include "description.hpp"
#include "mediahandler.hpp"
#include "queue.hpp"
#if RTC_ENABLE_MEDIA
#include "dtlssrtptransport.hpp"
#endif
#include <atomic>
#include <shared_mutex>
#include <variant>
namespace rtc::impl {
class Track final : public std::enable_shared_from_this<Track>, public Channel {
public:
Track(Description::Media description);
~Track() = default;
void close();
void incoming(message_ptr message);
bool outgoing(message_ptr message);
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
size_t availableAmount() const override;
bool isOpen() const;
bool isClosed() const;
string mid() const;
Description::Direction direction() const;
Description::Media description() const;
void setDescription(Description::Media description);
std::shared_ptr<MediaHandler> getRtcpHandler();
void setRtcpHandler(shared_ptr<MediaHandler> handler);
#if RTC_ENABLE_MEDIA
void open(std::shared_ptr<DtlsSrtpTransport> transport);
#endif
private:
#if RTC_ENABLE_MEDIA
weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
#endif
Description::Media mMediaDescription;
shared_ptr<MediaHandler> mRtcpHandler;
mutable std::shared_mutex mMutex;
std::atomic<bool> mIsClosed = false;
Queue<message_ptr> mRecvQueue;
};
} // namespace rtc::impl
#endif

View File

@ -16,17 +16,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_TRANSPORT_H
#define RTC_TRANSPORT_H
#ifndef RTC_IMPL_TRANSPORT_H
#define RTC_IMPL_TRANSPORT_H
#include "include.hpp"
#include "common.hpp"
#include "message.hpp"
#include <atomic>
#include <functional>
#include <memory>
namespace rtc {
namespace rtc::impl {
class Transport {
public:
@ -98,6 +98,6 @@ private:
std::atomic<bool> mStopped = true;
};
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -17,16 +17,11 @@
*/
#include "verifiedtlstransport.hpp"
#include "include.hpp"
#include "common.hpp"
#if RTC_ENABLE_WEBSOCKET
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::weak_ptr;
namespace rtc {
namespace rtc::impl {
VerifiedTlsTransport::VerifiedTlsTransport(shared_ptr<TcpTransport> lower, string host,
state_callback callback)
@ -44,6 +39,6 @@ VerifiedTlsTransport::VerifiedTlsTransport(shared_ptr<TcpTransport> lower, strin
VerifiedTlsTransport::~VerifiedTlsTransport() {}
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -16,14 +16,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_VERIFIED_TLS_TRANSPORT_H
#define RTC_VERIFIED_TLS_TRANSPORT_H
#ifndef RTC_IMPL_VERIFIED_TLS_TRANSPORT_H
#define RTC_IMPL_VERIFIED_TLS_TRANSPORT_H
#include "tlstransport.hpp"
#if RTC_ENABLE_WEBSOCKET
namespace rtc {
namespace rtc::impl {
class VerifiedTlsTransport final : public TlsTransport {
public:
@ -31,7 +31,7 @@ public:
~VerifiedTlsTransport();
};
} // namespace rtc
} // namespace rtc::impl
#endif

371
src/impl/websocket.cpp Normal file
View File

@ -0,0 +1,371 @@
/**
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if RTC_ENABLE_WEBSOCKET
#include "websocket.hpp"
#include "globals.hpp"
#include "common.hpp"
#include "threadpool.hpp"
#include "tcptransport.hpp"
#include "tlstransport.hpp"
#include "verifiedtlstransport.hpp"
#include "wstransport.hpp"
#include <regex>
#ifdef _WIN32
#include <winsock2.h>
#endif
namespace rtc::impl {
using namespace std::placeholders;
WebSocket::WebSocket(Configuration config_)
: config(std::move(config_)), mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {
PLOG_VERBOSE << "Creating WebSocket";
}
WebSocket::~WebSocket() {
PLOG_VERBOSE << "Destroying WebSocket";
remoteClose();
}
void WebSocket::parse(const string &url) {
PLOG_VERBOSE << "Opening WebSocket to URL: " << url;
if (state != State::Closed)
throw std::logic_error("WebSocket must be closed before opening");
// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
static const char *rs =
R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
static const std::regex r(rs, std::regex::extended);
std::smatch m;
if (!std::regex_match(url, m, r) || m[10].length() == 0)
throw std::invalid_argument("Invalid WebSocket URL: " + url);
mScheme = m[2];
if (mScheme.empty())
mScheme = "ws";
else if (mScheme != "ws" && mScheme != "wss")
throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme);
mHostname = m[10];
mService = m[12];
if (mService.empty()) {
mService = mScheme == "ws" ? "80" : "443";
mHost = mHostname;
} else {
mHost = mHostname + ':' + mService;
}
while (!mHostname.empty() && mHostname.front() == '[')
mHostname.erase(mHostname.begin());
while (!mHostname.empty() && mHostname.back() == ']')
mHostname.pop_back();
mPath = m[13];
if (mPath.empty())
mPath += '/';
if (string query = m[15]; !query.empty())
mPath += "?" + query;
changeState(State::Connecting);
initTcpTransport();
}
void WebSocket::close() {
auto s = state.load();
if (s == State::Connecting || s == State::Open) {
PLOG_VERBOSE << "Closing WebSocket";
changeState(State::Closing);
if (auto transport = std::atomic_load(&mWsTransport))
transport->close();
else
changeState(State::Closed);
}
}
void WebSocket::remoteClose() {
if (state.load() != State::Closed) {
close();
closeTransports();
}
}
bool WebSocket::isOpen() const { return state == State::Open; }
bool WebSocket::isClosed() const { return state == State::Closed; }
size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
std::optional<message_variant> WebSocket::receive() {
while (auto next = mRecvQueue.tryPop()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
}
return nullopt;
}
std::optional<message_variant> WebSocket::peek() {
while (auto next = mRecvQueue.peek()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
mRecvQueue.tryPop();
}
return nullopt;
}
size_t WebSocket::availableAmount() const { return mRecvQueue.amount(); }
bool WebSocket::changeState(State newState) { return state.exchange(newState) != newState; }
bool WebSocket::outgoing(message_ptr message) {
if (state != State::Open || !mWsTransport)
throw std::runtime_error("WebSocket is not open");
if (message->size() > maxMessageSize())
throw std::runtime_error("Message size exceeds limit");
return mWsTransport->send(message);
}
void WebSocket::incoming(message_ptr message) {
if (!message) {
remoteClose();
return;
}
if (message->type == Message::String || message->type == Message::Binary) {
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
}
}
shared_ptr<TcpTransport> WebSocket::initTcpTransport() {
PLOG_VERBOSE << "Starting TCP transport";
using State = TcpTransport::State;
try {
if (auto transport = std::atomic_load(&mTcpTransport))
return transport;
auto transport = std::make_shared<TcpTransport>(
mHostname, mService, [this, weak_this = weak_from_this()](State transportState) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (transportState) {
case State::Connected:
if (mScheme == "ws")
initWsTransport();
else
initTlsTransport();
break;
case State::Failed:
triggerError("TCP connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
});
std::atomic_store(&mTcpTransport, transport);
if (state == WebSocket::State::Closed) {
mTcpTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("TCP transport initialization failed");
}
}
shared_ptr<TlsTransport> WebSocket::initTlsTransport() {
PLOG_VERBOSE << "Starting TLS transport";
using State = TlsTransport::State;
try {
if (auto transport = std::atomic_load(&mTlsTransport))
return transport;
auto lower = std::atomic_load(&mTcpTransport);
auto stateChangeCallback = [this, weak_this = weak_from_this()](State transportState) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (transportState) {
case State::Connected:
initWsTransport();
break;
case State::Failed:
triggerError("TCP connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
};
shared_ptr<TlsTransport> transport;
#ifdef _WIN32
if (!config.disableTlsVerification) {
PLOG_WARNING << "TLS certificate verification with root CA is not supported on Windows";
}
transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
#else
if (config.disableTlsVerification)
transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
else
transport =
std::make_shared<VerifiedTlsTransport>(lower, mHostname, stateChangeCallback);
#endif
std::atomic_store(&mTlsTransport, transport);
if (state == WebSocket::State::Closed) {
mTlsTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("TLS transport initialization failed");
}
}
shared_ptr<WsTransport> WebSocket::initWsTransport() {
PLOG_VERBOSE << "Starting WebSocket transport";
using State = WsTransport::State;
try {
if (auto transport = std::atomic_load(&mWsTransport))
return transport;
shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport);
if (!lower)
lower = std::atomic_load(&mTcpTransport);
WsTransport::Configuration wsConfig = {};
wsConfig.host = mHost;
wsConfig.path = mPath;
wsConfig.protocols = config.protocols;
auto transport = std::make_shared<WsTransport>(
lower, wsConfig, weak_bind(&WebSocket::incoming, this, _1),
[this, weak_this = weak_from_this()](State transportState) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (transportState) {
case State::Connected:
if (state == WebSocket::State::Connecting) {
PLOG_DEBUG << "WebSocket open";
changeState(WebSocket::State::Open);
triggerOpen();
}
break;
case State::Failed:
triggerError("WebSocket connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
});
std::atomic_store(&mWsTransport, transport);
if (state == WebSocket::State::Closed) {
mWsTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("WebSocket transport initialization failed");
}
}
std::shared_ptr<TcpTransport> WebSocket::getTcpTransport() const {
return std::atomic_load(&mTcpTransport);
}
std::shared_ptr<TlsTransport> WebSocket::getTlsTransport() const {
return std::atomic_load(&mTlsTransport);
}
std::shared_ptr<WsTransport> WebSocket::getWsTransport() const {
return std::atomic_load(&mWsTransport);
}
void WebSocket::closeTransports() {
PLOG_VERBOSE << "Closing transports";
if (state.load() != State::Closed) {
changeState(State::Closed);
triggerClosed();
}
// Reset callbacks now that state is changed
resetCallbacks();
// Pass the pointers to a thread, allowing to terminate a transport from its own thread
auto ws = std::atomic_exchange(&mWsTransport, decltype(mWsTransport)(nullptr));
auto tls = std::atomic_exchange(&mTlsTransport, decltype(mTlsTransport)(nullptr));
auto tcp = std::atomic_exchange(&mTcpTransport, decltype(mTcpTransport)(nullptr));
ThreadPool::Instance().enqueue([ws, tls, tcp]() mutable {
if (ws)
ws->stop();
if (tls)
tls->stop();
if (tcp)
tcp->stop();
ws.reset();
tls.reset();
tcp.reset();
});
}
} // namespace rtc::impl
#endif

93
src/impl/websocket.hpp Normal file
View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_IMPL_WEBSOCKET_H
#define RTC_IMPL_WEBSOCKET_H
#if RTC_ENABLE_WEBSOCKET
#include "channel.hpp"
#include "common.hpp"
#include "init.hpp"
#include "message.hpp"
#include "queue.hpp"
#include "tcptransport.hpp"
#include "tlstransport.hpp"
#include "wstransport.hpp"
#include "rtc/websocket.hpp"
#include <atomic>
#include <optional>
#include <thread>
#include <variant>
namespace rtc::impl {
struct WebSocket final : public Channel, public std::enable_shared_from_this<WebSocket> {
using State = rtc::WebSocket::State;
using Configuration = rtc::WebSocket::Configuration;
WebSocket(Configuration config_);
~WebSocket();
void parse(const string &url);
void close();
bool outgoing(message_ptr message);
void incoming(message_ptr message);
std::optional<message_variant> receive() override;
std::optional<message_variant> peek() override;
size_t availableAmount() const override;
bool isOpen() const;
bool isClosed() const;
size_t maxMessageSize() const;
bool changeState(State state);
void remoteClose();
std::shared_ptr<TcpTransport> initTcpTransport();
std::shared_ptr<TlsTransport> initTlsTransport();
std::shared_ptr<WsTransport> initWsTransport();
std::shared_ptr<TcpTransport> getTcpTransport() const;
std::shared_ptr<TlsTransport> getTlsTransport() const;
std::shared_ptr<WsTransport> getWsTransport() const;
void closeTransports();
const Configuration config;
std::atomic<State> state = State::Closed;
private:
const init_token mInitToken = Init::Token();
std::shared_ptr<TcpTransport> mTcpTransport;
std::shared_ptr<TlsTransport> mTlsTransport;
std::shared_ptr<WsTransport> mWsTransport;
string mScheme, mHost, mHostname, mService, mPath;
Queue<message_ptr> mRecvQueue;
};
} // namespace rtc::impl
#endif
#endif // RTC_IMPL_WEBSOCKET_H

View File

@ -45,7 +45,7 @@
#define ntohll(x) htonll(x)
#endif
namespace rtc {
namespace rtc::impl {
using namespace std::chrono;
using std::to_integer;

View File

@ -16,15 +16,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RTC_WS_TRANSPORT_H
#define RTC_WS_TRANSPORT_H
#ifndef RTC_IMPL_WS_TRANSPORT_H
#define RTC_IMPL_WS_TRANSPORT_H
#include "include.hpp"
#include "common.hpp"
#include "transport.hpp"
#if RTC_ENABLE_WEBSOCKET
namespace rtc {
namespace rtc::impl {
class TcpTransport;
class TlsTransport;
@ -81,7 +81,7 @@ private:
Opcode mPartialOpcode;
};
} // namespace rtc
} // namespace rtc::impl
#endif

View File

@ -17,27 +17,26 @@
*/
#include "init.hpp"
#include "globals.hpp"
#include "certificate.hpp"
#include "dtlstransport.hpp"
#include "sctptransport.hpp"
#include "threadpool.hpp"
#include "tls.hpp"
#include "impl/certificate.hpp"
#include "impl/dtlstransport.hpp"
#include "impl/sctptransport.hpp"
#include "impl/threadpool.hpp"
#include "impl/tls.hpp"
#if RTC_ENABLE_WEBSOCKET
#include "tlstransport.hpp"
#include "impl/tlstransport.hpp"
#endif
#if RTC_ENABLE_MEDIA
#include "dtlssrtptransport.hpp"
#include "impl/dtlssrtptransport.hpp"
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
using std::shared_ptr;
namespace rtc {
namespace {
@ -51,7 +50,7 @@ void doInit() {
throw std::runtime_error("WSAStartup failed, error=" + std::to_string(WSAGetLastError()));
#endif
ThreadPool::Instance().spawn(THREADPOOL_SIZE);
impl::ThreadPool::Instance().spawn(THREADPOOL_SIZE);
#if USE_GNUTLS
// Nothing to do
@ -59,29 +58,29 @@ void doInit() {
openssl::init();
#endif
SctpTransport::Init();
DtlsTransport::Init();
impl::SctpTransport::Init();
impl::DtlsTransport::Init();
#if RTC_ENABLE_WEBSOCKET
TlsTransport::Init();
impl::TlsTransport::Init();
#endif
#if RTC_ENABLE_MEDIA
DtlsSrtpTransport::Init();
impl::DtlsSrtpTransport::Init();
#endif
}
void doCleanup() {
PLOG_DEBUG << "Global cleanup";
ThreadPool::Instance().join();
CleanupCertificateCache();
impl::ThreadPool::Instance().join();
impl::CleanupCertificateCache();
SctpTransport::Cleanup();
DtlsTransport::Cleanup();
impl::SctpTransport::Cleanup();
impl::DtlsTransport::Cleanup();
#if RTC_ENABLE_WEBSOCKET
TlsTransport::Cleanup();
impl::TlsTransport::Cleanup();
#endif
#if RTC_ENABLE_MEDIA
DtlsSrtpTransport::Cleanup();
impl::DtlsSrtpTransport::Cleanup();
#endif
#ifdef _WIN32
@ -114,7 +113,7 @@ void Init::Preload() {
Global = new shared_ptr<void>(token);
PLOG_DEBUG << "Preloading certificate";
make_certificate().wait();
impl::make_certificate().wait();
}
void Init::Cleanup() {

File diff suppressed because it is too large Load Diff

View File

@ -20,9 +20,10 @@
#if RTC_ENABLE_MEDIA
#include "rtcpreceivingsession.hpp"
#include "logcounter.hpp"
#include "track.hpp"
#include "impl/logcounter.hpp"
#include <cmath>
#include <utility>
@ -34,10 +35,10 @@
namespace rtc {
static LogCounter COUNTER_BAD_RTP_HEADER(plog::warning, "Number of malformed RTP headers");
static LogCounter COUNTER_UNKNOWN_PPID(plog::warning, "Number of Unknown PPID messages");
static LogCounter COUNTER_BAD_NOTIF_LEN(plog::warning, "Number of Bad-Lengthed notifications");
static LogCounter COUNTER_BAD_SCTP_STATUS(plog::warning, "Number of unknown SCTP_STATUS errors");
static impl::LogCounter COUNTER_BAD_RTP_HEADER(plog::warning, "Number of malformed RTP headers");
static impl::LogCounter COUNTER_UNKNOWN_PPID(plog::warning, "Number of Unknown PPID messages");
static impl::LogCounter COUNTER_BAD_NOTIF_LEN(plog::warning, "Number of Bad-Lengthed notifications");
static impl::LogCounter COUNTER_BAD_SCTP_STATUS(plog::warning, "Number of unknown SCTP_STATUS errors");
message_ptr RtcpReceivingSession::outgoing(message_ptr ptr) { return ptr; }

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -17,189 +17,50 @@
*/
#include "track.hpp"
#include "dtlssrtptransport.hpp"
#include "include.hpp"
#include "logcounter.hpp"
static rtc::LogCounter
COUNTER_MEDIA_BAD_DIRECTION(plog::warning,
"Number of media packets sent in invalid directions");
static rtc::LogCounter COUNTER_QUEUE_FULL(plog::warning,
"Number of media packets dropped due to a full queue");
#include "impl/track.hpp"
namespace rtc {
using std::shared_ptr;
using std::weak_ptr;
Track::Track(impl_ptr<impl::Track> impl)
: CheshireCat<impl::Track>(impl), Channel(std::dynamic_pointer_cast<impl::Channel>(impl)) {}
Track::Track(Description::Media description)
: mMediaDescription(std::move(description)), mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
string Track::mid() const { return impl()->mid(); }
string Track::mid() const {
std::shared_lock lock(mMutex);
return mMediaDescription.mid();
}
Description::Direction Track::direction() const { return impl()->direction(); }
Description::Media Track::description() const {
std::shared_lock lock(mMutex);
return mMediaDescription;
}
Description::Direction Track::direction() const {
std::shared_lock lock(mMutex);
return mMediaDescription.direction();
}
Description::Media Track::description() const { return impl()->description(); }
void Track::setDescription(Description::Media description) {
std::unique_lock lock(mMutex);
if (description.mid() != mMediaDescription.mid())
throw std::logic_error("Media description mid does not match track mid");
mMediaDescription = std::move(description);
impl()->setDescription(std::move(description));
}
void Track::close() {
mIsClosed = true;
void Track::close() { impl()->close(); }
setRtcpHandler(nullptr);
resetCallbacks();
}
bool Track::send(message_variant data) {
if (mIsClosed)
throw std::runtime_error("Track is closed");
auto dir = direction();
if ((dir == Description::Direction::RecvOnly || dir == Description::Direction::Inactive)) {
COUNTER_MEDIA_BAD_DIRECTION++;
return false;
}
auto message = make_message(std::move(data));
if (auto handler = getRtcpHandler()) {
message = handler->outgoing(message);
if (!message)
return false;
}
return outgoing(std::move(message));
}
bool Track::send(message_variant data) { return impl()->outgoing(make_message(std::move(data))); }
bool Track::send(const byte *data, size_t size) { return send(binary(data, data + size)); }
std::optional<message_variant> Track::receive() {
if (auto next = mRecvQueue.tryPop())
return to_variant(std::move(**next));
bool Track::isOpen(void) const { return impl()->isOpen(); }
return nullopt;
}
std::optional<message_variant> Track::peek() {
if (auto next = mRecvQueue.peek())
return to_variant(std::move(**next));
return nullopt;
}
bool Track::isOpen(void) const {
#if RTC_ENABLE_MEDIA
std::shared_lock lock(mMutex);
return !mIsClosed && mDtlsSrtpTransport.lock();
#else
return !mIsClosed;
#endif
}
bool Track::isClosed(void) const { return mIsClosed; }
bool Track::isClosed(void) const { return impl()->isClosed(); }
size_t Track::maxMessageSize() const {
return 65535 - 12 - 4; // SRTP/UDP
}
size_t Track::availableAmount() const { return mRecvQueue.amount(); }
#if RTC_ENABLE_MEDIA
void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
{
std::lock_guard lock(mMutex);
mDtlsSrtpTransport = transport;
}
triggerOpen();
}
#endif
void Track::incoming(message_ptr message) {
if (!message)
return;
auto dir = direction();
if ((dir == Description::Direction::SendOnly || dir == Description::Direction::Inactive) &&
message->type != Message::Control) {
COUNTER_MEDIA_BAD_DIRECTION++;
return;
}
if (auto handler = getRtcpHandler()) {
message = handler->incoming(message);
if (!message)
return;
}
// Tail drop if queue is full
if (mRecvQueue.full()) {
COUNTER_QUEUE_FULL++;
return;
}
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
}
bool Track::outgoing([[maybe_unused]] message_ptr message) {
#if RTC_ENABLfiE_MEDIA
std::shared_ptr<DtlsSrtpTransport> transport;
{
std::shared_lock lock(mMutex);
transport = mDtlsSrtpTransport.lock();
if (!transport)
throw std::runtime_error("Track is closed");
// Set recommended medium-priority DSCP value
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
if (mMediaDescription.type() == "audio")
message->dscp = 46; // EF: Expedited Forwarding
else
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
}
return transport->sendMedia(message);
#else
PLOG_WARNING << "Ignoring track send (not compiled with media support)";
return false;
#endif
// TODO
return 65535;
}
void Track::setRtcpHandler(std::shared_ptr<MediaHandler> handler) {
{
std::unique_lock lock(mMutex);
mRtcpHandler = handler;
}
handler->onOutgoing(std::bind(&Track::outgoing, this, std::placeholders::_1));
impl()->setRtcpHandler(std::move(handler));
}
bool Track::requestKeyframe() {
if (auto handler = getRtcpHandler())
if (auto handler = impl()->getRtcpHandler())
return handler->requestKeyframe();
return false;
}
std::shared_ptr<MediaHandler> Track::getRtcpHandler() {
std::shared_lock lock(mMutex);
return mRtcpHandler;
}
std::shared_ptr<MediaHandler> Track::getRtcpHandler() { return impl()->getRtcpHandler(); }
} // namespace rtc

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,13 +19,10 @@
#if RTC_ENABLE_WEBSOCKET
#include "websocket.hpp"
#include "include.hpp"
#include "threadpool.hpp"
#include "globals.hpp"
#include "common.hpp"
#include "tcptransport.hpp"
#include "tlstransport.hpp"
#include "verifiedtlstransport.hpp"
#include "wstransport.hpp"
#include "impl/websocket.hpp"
#include <regex>
@ -35,335 +32,50 @@
namespace rtc {
using std::shared_ptr;
using namespace std::placeholders;
WebSocket::WebSocket(std::optional<Configuration> config)
: mConfig(config ? std::move(*config) : Configuration()),
mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {
PLOG_VERBOSE << "Creating WebSocket";
}
WebSocket::WebSocket() : WebSocket(Configuration()) {}
WebSocket::~WebSocket() {
PLOG_VERBOSE << "Destroying WebSocket";
remoteClose();
}
WebSocket::WebSocket(Configuration config)
: CheshireCat<impl::WebSocket>(std::move(config)),
Channel(std::dynamic_pointer_cast<impl::Channel>(CheshireCat<impl::WebSocket>::impl())) {}
WebSocket::State WebSocket::readyState() const { return mState; }
WebSocket::~WebSocket() { impl()->remoteClose(); }
WebSocket::State WebSocket::readyState() const { return impl()->state; }
bool WebSocket::isOpen() const { return impl()->state.load() == State::Open; }
bool WebSocket::isClosed() const { return impl()->state.load() == State::Closed; }
size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
void WebSocket::open(const string &url) {
PLOG_VERBOSE << "Opening WebSocket to URL: " << url;
if (mState != State::Closed)
throw std::logic_error("WebSocket must be closed before opening");
// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
static const char *rs =
R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
static const std::regex r(rs, std::regex::extended);
std::smatch m;
if (!std::regex_match(url, m, r) || m[10].length() == 0)
throw std::invalid_argument("Invalid WebSocket URL: " + url);
mScheme = m[2];
if (mScheme.empty())
mScheme = "ws";
else if (mScheme != "ws" && mScheme != "wss")
throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme);
mHostname = m[10];
mService = m[12];
if (mService.empty()) {
mService = mScheme == "ws" ? "80" : "443";
mHost = mHostname;
} else {
mHost = mHostname + ':' + mService;
}
while (!mHostname.empty() && mHostname.front() == '[')
mHostname.erase(mHostname.begin());
while (!mHostname.empty() && mHostname.back() == ']')
mHostname.pop_back();
mPath = m[13];
if (mPath.empty())
mPath += '/';
if (string query = m[15]; !query.empty())
mPath += "?" + query;
changeState(State::Connecting);
initTcpTransport();
impl()->parse(url);
impl()->changeState(State::Connecting);
impl()->initTcpTransport();
}
void WebSocket::close() {
auto state = mState.load();
auto state = impl()->state.load();
if (state == State::Connecting || state == State::Open) {
PLOG_VERBOSE << "Closing WebSocket";
changeState(State::Closing);
if (auto transport = std::atomic_load(&mWsTransport))
impl()->changeState(State::Closing);
if (auto transport = impl()->getWsTransport())
transport->close();
else
changeState(State::Closed);
impl()->changeState(State::Closed);
}
}
void WebSocket::remoteClose() {
if (mState.load() != State::Closed) {
close();
closeTransports();
}
bool WebSocket::send(message_variant data) {
return impl()->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::isClosed() const { return mState == State::Closed; }
size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
std::optional<message_variant> WebSocket::receive() {
while (auto next = mRecvQueue.tryPop()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
}
return nullopt;
}
std::optional<message_variant> WebSocket::peek() {
while (auto next = mRecvQueue.peek()) {
message_ptr message = *next;
if (message->type != Message::Control)
return to_variant(std::move(*message));
mRecvQueue.tryPop();
}
return nullopt;
}
size_t WebSocket::availableAmount() const { return mRecvQueue.amount(); }
bool WebSocket::changeState(State state) { return mState.exchange(state) != state; }
bool WebSocket::outgoing(message_ptr message) {
if (mState != State::Open || !mWsTransport)
throw std::runtime_error("WebSocket is not open");
if (message->size() > maxMessageSize())
throw std::runtime_error("Message size exceeds limit");
return mWsTransport->send(message);
}
void WebSocket::incoming(message_ptr message) {
if (!message) {
remoteClose();
return;
}
if (message->type == Message::String || message->type == Message::Binary) {
mRecvQueue.push(message);
triggerAvailable(mRecvQueue.size());
}
}
shared_ptr<TcpTransport> WebSocket::initTcpTransport() {
PLOG_VERBOSE << "Starting TCP transport";
using State = TcpTransport::State;
try {
std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mTcpTransport))
return transport;
auto transport = std::make_shared<TcpTransport>(
mHostname, mService, [this, weak_this = weak_from_this()](State state) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (state) {
case State::Connected:
if (mScheme == "ws")
initWsTransport();
else
initTlsTransport();
break;
case State::Failed:
triggerError("TCP connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
});
std::atomic_store(&mTcpTransport, transport);
if (mState == WebSocket::State::Closed) {
mTcpTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("TCP transport initialization failed");
}
}
shared_ptr<TlsTransport> WebSocket::initTlsTransport() {
PLOG_VERBOSE << "Starting TLS transport";
using State = TlsTransport::State;
try {
std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mTlsTransport))
return transport;
auto lower = std::atomic_load(&mTcpTransport);
auto stateChangeCallback = [this, weak_this = weak_from_this()](State state) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (state) {
case State::Connected:
initWsTransport();
break;
case State::Failed:
triggerError("TCP connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
};
shared_ptr<TlsTransport> transport;
#ifdef _WIN32
if (!mConfig.disableTlsVerification) {
PLOG_WARNING << "TLS certificate verification with root CA is not supported on Windows";
}
transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
#else
if (mConfig.disableTlsVerification)
transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
else
transport =
std::make_shared<VerifiedTlsTransport>(lower, mHostname, stateChangeCallback);
#endif
std::atomic_store(&mTlsTransport, transport);
if (mState == WebSocket::State::Closed) {
mTlsTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("TLS transport initialization failed");
}
}
shared_ptr<WsTransport> WebSocket::initWsTransport() {
PLOG_VERBOSE << "Starting WebSocket transport";
using State = WsTransport::State;
try {
std::lock_guard lock(mInitMutex);
if (auto transport = std::atomic_load(&mWsTransport))
return transport;
shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport);
if (!lower)
lower = std::atomic_load(&mTcpTransport);
WsTransport::Configuration wsConfig = {};
wsConfig.host = mHost;
wsConfig.path = mPath;
wsConfig.protocols = mConfig.protocols;
auto transport = std::make_shared<WsTransport>(
lower, wsConfig, weak_bind(&WebSocket::incoming, this, _1),
[this, weak_this = weak_from_this()](State state) {
auto shared_this = weak_this.lock();
if (!shared_this)
return;
switch (state) {
case State::Connected:
if (mState == WebSocket::State::Connecting) {
PLOG_DEBUG << "WebSocket open";
changeState(WebSocket::State::Open);
triggerOpen();
}
break;
case State::Failed:
triggerError("WebSocket connection failed");
remoteClose();
break;
case State::Disconnected:
remoteClose();
break;
default:
// Ignore
break;
}
});
std::atomic_store(&mWsTransport, transport);
if (mState == WebSocket::State::Closed) {
mWsTransport.reset();
throw std::runtime_error("Connection is closed");
}
transport->start();
return transport;
} catch (const std::exception &e) {
PLOG_ERROR << e.what();
remoteClose();
throw std::runtime_error("WebSocket transport initialization failed");
}
}
void WebSocket::closeTransports() {
PLOG_VERBOSE << "Closing transports";
if (mState.load() != State::Closed) {
changeState(State::Closed);
triggerClosed();
}
// Reset callbacks now that state is changed
resetCallbacks();
// Pass the pointers to a thread, allowing to terminate a transport from its own thread
auto ws = std::atomic_exchange(&mWsTransport, decltype(mWsTransport)(nullptr));
auto tls = std::atomic_exchange(&mTlsTransport, decltype(mTlsTransport)(nullptr));
auto tcp = std::atomic_exchange(&mTcpTransport, decltype(mTcpTransport)(nullptr));
ThreadPool::Instance().enqueue([ws, tls, tcp]() mutable {
if (ws)
ws->stop();
if (tls)
tls->stop();
if (tcp)
tcp->stop();
ws.reset();
tls.reset();
tcp.reset();
});
return impl()->outgoing(make_message(data, data + size));
}
} // namespace rtc

View File

@ -42,53 +42,41 @@ size_t benchmark(milliseconds duration) {
// config1.iceServers.emplace_back("stun:stun.l.google.com:19302");
// config1.mtu = 1500;
auto pc1 = std::make_shared<PeerConnection>(config1);
PeerConnection pc1(config1);
Configuration config2;
// config2.iceServers.emplace_back("stun:stun.l.google.com:19302");
// config2.mtu = 1500;
auto pc2 = std::make_shared<PeerConnection>(config2);
PeerConnection pc2(config2);
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](Description sdp) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalDescription([&pc2](Description sdp) {
cout << "Description 1: " << sdp << endl;
pc2->setRemoteDescription(std::move(sdp));
pc2.setRemoteDescription(std::move(sdp));
});
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](Candidate candidate) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalCandidate([&pc2](Candidate candidate) {
cout << "Candidate 1: " << candidate << endl;
pc2->addRemoteCandidate(std::move(candidate));
pc2.addRemoteCandidate(std::move(candidate));
});
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
});
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](Description sdp) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalDescription([&pc1](Description sdp) {
cout << "Description 2: " << sdp << endl;
pc1->setRemoteDescription(std::move(sdp));
pc1.setRemoteDescription(std::move(sdp));
});
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](Candidate candidate) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalCandidate([&pc1](Candidate candidate) {
cout << "Candidate 2: " << candidate << endl;
pc1->addRemoteCandidate(std::move(candidate));
pc1.addRemoteCandidate(std::move(candidate));
});
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
@ -101,7 +89,7 @@ size_t benchmark(milliseconds duration) {
steady_clock::time_point startTime, openTime, receivedTime, endTime;
shared_ptr<DataChannel> dc2;
pc2->onDataChannel([&dc2, &receivedSize, &receivedTime](shared_ptr<DataChannel> dc) {
pc2.onDataChannel([&dc2, &receivedSize, &receivedTime](shared_ptr<DataChannel> dc) {
dc->onMessage([&receivedTime, &receivedSize](variant<binary, string> message) {
if (holds_alternative<binary>(message)) {
const auto &bin = get<binary>(message);
@ -117,7 +105,7 @@ size_t benchmark(milliseconds duration) {
});
startTime = steady_clock::now();
auto dc1 = pc1->createDataChannel("benchmark");
auto dc1 = pc1.createDataChannel("benchmark");
dc1->onOpen([wdc1 = make_weak_ptr(dc1), &messageData, &openTime]() {
auto dc1 = wdc1.lock();
@ -169,8 +157,8 @@ size_t benchmark(milliseconds duration) {
cout << "Goodput: " << goodput * 0.001 << " MB/s"
<< " (" << goodput * 0.001 * 8 << " Mbit/s)" << endl;
pc1->close();
pc2->close();
pc1.close();
pc2.close();
rtc::Cleanup();
this_thread::sleep_for(1s);

View File

@ -39,7 +39,7 @@ void test_connectivity() {
// Custom MTU example
config1.mtu = 1500;
auto pc1 = std::make_shared<PeerConnection>(config1);
PeerConnection pc1(config1);
Configuration config2;
// STUN server example (not necessary to connect locally)
@ -51,62 +51,50 @@ void test_connectivity() {
config2.portRangeBegin = 5000;
config2.portRangeEnd = 6000;
auto pc2 = std::make_shared<PeerConnection>(config2);
PeerConnection pc2(config2);
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](Description sdp) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalDescription([&pc2](Description sdp) {
cout << "Description 1: " << sdp << endl;
pc2->setRemoteDescription(string(sdp));
pc2.setRemoteDescription(string(sdp));
});
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](Candidate candidate) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalCandidate([&pc2](Candidate candidate) {
cout << "Candidate 1: " << candidate << endl;
pc2->addRemoteCandidate(string(candidate));
pc2.addRemoteCandidate(string(candidate));
});
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc1.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
});
pc1->onSignalingStateChange([](PeerConnection::SignalingState state) {
pc1.onSignalingStateChange([](PeerConnection::SignalingState state) {
cout << "Signaling state 1: " << state << endl;
});
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](Description sdp) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalDescription([&pc1](Description sdp) {
cout << "Description 2: " << sdp << endl;
pc1->setRemoteDescription(string(sdp));
pc1.setRemoteDescription(string(sdp));
});
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](Candidate candidate) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalCandidate([&pc1](Candidate candidate) {
cout << "Candidate 2: " << candidate << endl;
pc1->addRemoteCandidate(string(candidate));
pc1.addRemoteCandidate(string(candidate));
});
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
pc2->onSignalingStateChange([](PeerConnection::SignalingState state) {
pc2.onSignalingStateChange([](PeerConnection::SignalingState state) {
cout << "Signaling state 2: " << state << endl;
});
shared_ptr<DataChannel> dc2;
pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
pc2.onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
cout << "DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
if (dc->label() != "test") {
cerr << "Wrong DataChannel label" << endl;
@ -124,7 +112,7 @@ void test_connectivity() {
std::atomic_store(&dc2, dc);
});
auto dc1 = pc1->createDataChannel("test");
auto dc1 = pc1.createDataChannel("test");
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
auto dc1 = wdc1.lock();
if (!dc1)
@ -145,35 +133,35 @@ void test_connectivity() {
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
this_thread::sleep_for(1s);
if (pc1->state() != PeerConnection::State::Connected &&
pc2->state() != PeerConnection::State::Connected)
if (pc1.state() != PeerConnection::State::Connected &&
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");
if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
throw runtime_error("DataChannel is not open");
if (auto addr = pc1->localAddress())
if (auto addr = pc1.localAddress())
cout << "Local address 1: " << *addr << endl;
if (auto addr = pc1->remoteAddress())
if (auto addr = pc1.remoteAddress())
cout << "Remote address 1: " << *addr << endl;
if (auto addr = pc2->localAddress())
if (auto addr = pc2.localAddress())
cout << "Local address 2: " << *addr << endl;
if (auto addr = pc2->remoteAddress())
if (auto addr = pc2.remoteAddress())
cout << "Remote address 2: " << *addr << endl;
Candidate local, remote;
if (pc1->getSelectedCandidatePair(&local, &remote)) {
if (pc1.getSelectedCandidatePair(&local, &remote)) {
cout << "Local candidate 1: " << local << endl;
cout << "Remote candidate 1: " << remote << endl;
}
if (pc2->getSelectedCandidatePair(&local, &remote)) {
if (pc2.getSelectedCandidatePair(&local, &remote)) {
cout << "Local candidate 2: " << local << endl;
cout << "Remote candidate 2: " << remote << endl;
}
// Try to open a second data channel with another label
shared_ptr<DataChannel> second2;
pc2->onDataChannel([&second2](shared_ptr<DataChannel> dc) {
pc2.onDataChannel([&second2](shared_ptr<DataChannel> dc) {
cout << "Second DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
if (dc->label() != "second") {
cerr << "Wrong second DataChannel label" << endl;
@ -191,7 +179,7 @@ void test_connectivity() {
std::atomic_store(&second2, dc);
});
auto second1 = pc1->createDataChannel("second");
auto second1 = pc1.createDataChannel("second");
second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() {
auto second1 = wsecond1.lock();
if (!second1)
@ -221,8 +209,8 @@ void test_connectivity() {
DataChannelInit init;
init.negotiated = true;
init.id = 42;
auto negotiated1 = pc1->createDataChannel("negotiated", init);
auto negotiated2 = pc2->createDataChannel("negoctated", init);
auto negotiated1 = pc1.createDataChannel("negotiated", init);
auto negotiated2 = pc2.createDataChannel("negoctated", init);
if (!negotiated1->isOpen() || !negotiated2->isOpen())
throw runtime_error("Negotiated DataChannel is not open");
@ -246,9 +234,9 @@ void test_connectivity() {
throw runtime_error("Negotiated DataChannel failed");
// Delay close of peer 2 to check closing works properly
pc1->close();
pc1.close();
this_thread::sleep_for(1s);
pc2->close();
pc2.close();
this_thread::sleep_for(1s);
// You may call rtc::Cleanup() when finished to free static resources

View File

@ -36,7 +36,7 @@ void test_track() {
// STUN server example
// config1.iceServers.emplace_back("stun:stun.l.google.com:19302");
auto pc1 = std::make_shared<PeerConnection>(config1);
PeerConnection pc1(config1);
Configuration config2;
// STUN server example
@ -45,55 +45,43 @@ void test_track() {
config2.portRangeBegin = 5000;
config2.portRangeEnd = 6000;
auto pc2 = std::make_shared<PeerConnection>(config2);
PeerConnection pc2(config2);
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](Description sdp) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalDescription([&pc2](Description sdp) {
cout << "Description 1: " << sdp << endl;
pc2->setRemoteDescription(string(sdp));
pc2.setRemoteDescription(string(sdp));
});
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](Candidate candidate) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalCandidate([&pc2](Candidate candidate) {
cout << "Candidate 1: " << candidate << endl;
pc2->addRemoteCandidate(string(candidate));
pc2.addRemoteCandidate(string(candidate));
});
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc1.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
});
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](Description sdp) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalDescription([&pc1](Description sdp) {
cout << "Description 2: " << sdp << endl;
pc1->setRemoteDescription(string(sdp));
pc1.setRemoteDescription(string(sdp));
});
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](Candidate candidate) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalCandidate([&pc1](Candidate candidate) {
cout << "Candidate 2: " << candidate << endl;
pc1->addRemoteCandidate(string(candidate));
pc1.addRemoteCandidate(string(candidate));
});
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
shared_ptr<Track> t2;
string newTrackMid;
pc2->onTrack([&t2, &newTrackMid](shared_ptr<Track> t) {
pc2.onTrack([&t2, &newTrackMid](shared_ptr<Track> t) {
cout << "Track 2: Received with mid \"" << t->mid() << "\"" << endl;
if (t->mid() != newTrackMid) {
cerr << "Wrong track mid" << endl;
@ -105,17 +93,17 @@ void test_track() {
// Test opening a track
newTrackMid = "test";
auto t1 = pc1->addTrack(Description::Video(newTrackMid));
auto t1 = pc1.addTrack(Description::Video(newTrackMid));
pc1->setLocalDescription();
pc1.setLocalDescription();
int attempts = 10;
shared_ptr<Track> at2;
while ((!(at2 = std::atomic_load(&t2)) || !at2->isOpen() || !t1->isOpen()) && attempts--)
this_thread::sleep_for(1s);
if (pc1->state() != PeerConnection::State::Connected &&
pc2->state() != PeerConnection::State::Connected)
if (pc1.state() != PeerConnection::State::Connected &&
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");
if (!at2 || !at2->isOpen() || !t1->isOpen())
@ -123,9 +111,9 @@ void test_track() {
// Test renegotiation
newTrackMid = "added";
t1 = pc1->addTrack(Description::Video(newTrackMid));
t1 = pc1.addTrack(Description::Video(newTrackMid));
pc1->setLocalDescription();
pc1.setLocalDescription();
attempts = 10;
t2.reset();
@ -138,9 +126,9 @@ void test_track() {
// TODO: Test sending RTP packets in track
// Delay close of peer 2 to check closing works properly
pc1->close();
pc1.close();
this_thread::sleep_for(1s);
pc2->close();
pc2.close();
this_thread::sleep_for(1s);
// You may call rtc::Cleanup() when finished to free static resources

View File

@ -40,7 +40,7 @@ void test_turn_connectivity() {
// Please do not use outside of libdatachannel tests
config1.iceServers.emplace_back("turn:datachannel_test:14018314739877@stun.ageneau.net:3478");
auto pc1 = std::make_shared<PeerConnection>(config1);
PeerConnection pc1(config1);
Configuration config2;
// STUN server example (not necessary, just here for testing)
@ -50,70 +50,58 @@ void test_turn_connectivity() {
// Please do not use outside of libdatachannel tests
config2.iceServers.emplace_back("turn:datachannel_test:14018314739877@stun.ageneau.net:3478");
auto pc2 = std::make_shared<PeerConnection>(config2);
PeerConnection pc2(config2);
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](Description sdp) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalDescription([&pc2](Description sdp) {
cout << "Description 1: " << sdp << endl;
pc2->setRemoteDescription(string(sdp));
pc2.setRemoteDescription(string(sdp));
});
pc1->onLocalCandidate([wpc2 = make_weak_ptr(pc2)](Candidate candidate) {
auto pc2 = wpc2.lock();
if (!pc2)
return;
pc1.onLocalCandidate([&pc2](Candidate candidate) {
// For this test, filter out non-relay candidates to force TURN
string str(candidate);
if (str.find("relay") != string::npos) {
cout << "Candidate 1: " << str << endl;
pc2->addRemoteCandidate(str);
pc2.addRemoteCandidate(str);
}
});
pc1->onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
pc1->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc1.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
});
pc1->onSignalingStateChange([](PeerConnection::SignalingState state) {
pc1.onSignalingStateChange([](PeerConnection::SignalingState state) {
cout << "Signaling state 1: " << state << endl;
});
pc2->onLocalDescription([wpc1 = make_weak_ptr(pc1)](Description sdp) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalDescription([&pc1](Description sdp) {
cout << "Description 2: " << sdp << endl;
pc1->setRemoteDescription(string(sdp));
pc1.setRemoteDescription(string(sdp));
});
pc2->onLocalCandidate([wpc1 = make_weak_ptr(pc1)](Candidate candidate) {
auto pc1 = wpc1.lock();
if (!pc1)
return;
pc2.onLocalCandidate([&pc1](Candidate candidate) {
// For this test, filter out non-relay candidates to force TURN
string str(candidate);
if (str.find("relay") != string::npos) {
cout << "Candidate 1: " << str << endl;
pc1->addRemoteCandidate(str);
pc1.addRemoteCandidate(str);
}
});
pc2->onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
pc2->onGatheringStateChange([](PeerConnection::GatheringState state) {
pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
pc2->onSignalingStateChange([](PeerConnection::SignalingState state) {
pc2.onSignalingStateChange([](PeerConnection::SignalingState state) {
cout << "Signaling state 2: " << state << endl;
});
shared_ptr<DataChannel> dc2;
pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
pc2.onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
cout << "DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
if (dc->label() != "test") {
cerr << "Wrong DataChannel label" << endl;
@ -131,7 +119,7 @@ void test_turn_connectivity() {
std::atomic_store(&dc2, dc);
});
auto dc1 = pc1->createDataChannel("test");
auto dc1 = pc1.createDataChannel("test");
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
auto dc1 = wdc1.lock();
if (!dc1)
@ -152,35 +140,35 @@ void test_turn_connectivity() {
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
this_thread::sleep_for(1s);
if (pc1->state() != PeerConnection::State::Connected &&
pc2->state() != PeerConnection::State::Connected)
if (pc1.state() != PeerConnection::State::Connected &&
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");
if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
throw runtime_error("DataChannel is not open");
if (auto addr = pc1->localAddress())
if (auto addr = pc1.localAddress())
cout << "Local address 1: " << *addr << endl;
if (auto addr = pc1->remoteAddress())
if (auto addr = pc1.remoteAddress())
cout << "Remote address 1: " << *addr << endl;
if (auto addr = pc2->localAddress())
if (auto addr = pc2.localAddress())
cout << "Local address 2: " << *addr << endl;
if (auto addr = pc2->remoteAddress())
if (auto addr = pc2.remoteAddress())
cout << "Remote address 2: " << *addr << endl;
Candidate local, remote;
if (pc1->getSelectedCandidatePair(&local, &remote)) {
if (pc1.getSelectedCandidatePair(&local, &remote)) {
cout << "Local candidate 1: " << local << endl;
cout << "Remote candidate 1: " << remote << endl;
}
if (pc2->getSelectedCandidatePair(&local, &remote)) {
if (pc2.getSelectedCandidatePair(&local, &remote)) {
cout << "Local candidate 2: " << local << endl;
cout << "Remote candidate 2: " << remote << endl;
}
// Try to open a second data channel with another label
shared_ptr<DataChannel> second2;
pc2->onDataChannel([&second2](shared_ptr<DataChannel> dc) {
pc2.onDataChannel([&second2](shared_ptr<DataChannel> dc) {
cout << "Second DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
if (dc->label() != "second") {
cerr << "Wrong second DataChannel label" << endl;
@ -198,7 +186,7 @@ void test_turn_connectivity() {
std::atomic_store(&second2, dc);
});
auto second1 = pc1->createDataChannel("second");
auto second1 = pc1.createDataChannel("second");
second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() {
auto second1 = wsecond1.lock();
if (!second1)
@ -228,8 +216,8 @@ void test_turn_connectivity() {
DataChannelInit init;
init.negotiated = true;
init.id = 42;
auto negotiated1 = pc1->createDataChannel("negotiated", init);
auto negotiated2 = pc2->createDataChannel("negoctated", init);
auto negotiated1 = pc1.createDataChannel("negotiated", init);
auto negotiated2 = pc2.createDataChannel("negoctated", init);
if (!negotiated1->isOpen() || !negotiated2->isOpen())
throw runtime_error("Negotiated DataChannel is not open");
@ -253,9 +241,9 @@ void test_turn_connectivity() {
throw runtime_error("Negotiated DataChannel failed");
// Delay close of peer 2 to check closing works properly
pc1->close();
pc1.close();
this_thread::sleep_for(1s);
pc2->close();
pc2.close();
this_thread::sleep_for(1s);
// You may call rtc::Cleanup() when finished to free static resources

View File

@ -36,24 +36,20 @@ void test_websocket() {
const string myMessage = "Hello world from libdatachannel";
auto ws = std::make_shared<WebSocket>();
WebSocket ws;
// Certificate verification can be disabled
// auto ws = std::make_shared<WebSocket>(WebSocket::Configuration{.disableTlsVerification =
// true});
// WebSocket ws(WebSocket::Configuration{.disableTlsVerification = true});
ws->onOpen([wws = make_weak_ptr(ws), &myMessage]() {
auto ws = wws.lock();
if (!ws)
return;
ws.onOpen([&ws, &myMessage]() {
cout << "WebSocket: Open" << endl;
ws->send(myMessage);
ws.send(myMessage);
});
ws->onClosed([]() { cout << "WebSocket: Closed" << endl; });
ws.onClosed([]() { cout << "WebSocket: Closed" << endl; });
std::atomic<bool> received = false;
ws->onMessage([&received, &myMessage](variant<binary, string> message) {
ws.onMessage([&received, &myMessage](variant<binary, string> message) {
if (holds_alternative<string>(message)) {
string str = std::move(get<string>(message));
if ((received = (str == myMessage)))
@ -63,19 +59,19 @@ void test_websocket() {
}
});
ws->open("wss://echo.websocket.org:443/");
ws.open("wss://echo.websocket.org:443/");
int attempts = 10;
while ((!ws->isOpen() || !received) && attempts--)
while ((!ws.isOpen() || !received) && attempts--)
this_thread::sleep_for(1s);
if (!ws->isOpen())
if (!ws.isOpen())
throw runtime_error("WebSocket is not open");
if (!received)
throw runtime_error("Expected message not received");
ws->close();
ws.close();
this_thread::sleep_for(1s);
// You may call rtc::Cleanup() when finished to free static resources