mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-24 07:59:23 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
0fcafad9c7 | |||
aab876d346 | |||
11ec8f7247 | |||
1597c9ae6f | |||
b093c4c3d5 | |||
447624322c | |||
422713cbdc | |||
d3d4187021 | |||
f2dd46e589 | |||
5b5debf260 | |||
86c3f914fb | |||
6a1fff13c1 | |||
91a854aa5b | |||
1181fdc599 | |||
fe3d92cebf | |||
c06d77bd8e |
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
project(libdatachannel
|
project(libdatachannel
|
||||||
VERSION 0.10.0
|
VERSION 0.10.1
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
|
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
|
||||||
|
|
||||||
@ -115,17 +115,15 @@ set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
|
|||||||
add_subdirectory(deps/plog)
|
add_subdirectory(deps/plog)
|
||||||
|
|
||||||
option(sctp_build_programs 0)
|
option(sctp_build_programs 0)
|
||||||
|
option(sctp_build_shared_lib 0)
|
||||||
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
||||||
if (MSYS OR MINGW)
|
if (MSYS OR MINGW)
|
||||||
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
||||||
target_compile_definitions(usrsctp-static PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
|
||||||
endif()
|
endif()
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
|
target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
|
||||||
target_compile_options(usrsctp-static PRIVATE -Wno-error=format-truncation)
|
|
||||||
endif()
|
endif()
|
||||||
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
|
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
|
||||||
add_library(Usrsctp::UsrsctpStatic ALIAS usrsctp-static)
|
|
||||||
|
|
||||||
if (NO_WEBSOCKET)
|
if (NO_WEBSOCKET)
|
||||||
add_library(datachannel SHARED
|
add_library(datachannel SHARED
|
||||||
@ -156,13 +154,13 @@ target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includ
|
|||||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_link_libraries(datachannel PUBLIC Threads::Threads plog::plog)
|
target_link_libraries(datachannel PUBLIC Threads::Threads plog::plog)
|
||||||
target_link_libraries(datachannel PRIVATE Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel PRIVATE Usrsctp::Usrsctp)
|
||||||
|
|
||||||
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog)
|
target_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog)
|
||||||
target_link_libraries(datachannel-static PRIVATE Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel-static PRIVATE Usrsctp::Usrsctp)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(datachannel PRIVATE ws2_32) # winsock2
|
target_link_libraries(datachannel PRIVATE ws2_32) # winsock2
|
||||||
|
2
deps/usrsctp
vendored
2
deps/usrsctp
vendored
Submodule deps/usrsctp updated: 0db9691000...2e754d5822
@ -102,12 +102,12 @@ private:
|
|||||||
std::function<void()> function;
|
std::function<void()> function;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... P> class synchronized_callback {
|
template <typename... Args> class synchronized_callback {
|
||||||
public:
|
public:
|
||||||
synchronized_callback() = default;
|
synchronized_callback() = default;
|
||||||
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
|
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
|
||||||
synchronized_callback(const synchronized_callback &cb) { *this = cb; }
|
synchronized_callback(const synchronized_callback &cb) { *this = cb; }
|
||||||
synchronized_callback(std::function<void(P...)> func) { *this = std::move(func); }
|
synchronized_callback(std::function<void(Args...)> func) { *this = std::move(func); }
|
||||||
~synchronized_callback() { *this = nullptr; }
|
~synchronized_callback() { *this = nullptr; }
|
||||||
|
|
||||||
synchronized_callback &operator=(synchronized_callback &&cb) {
|
synchronized_callback &operator=(synchronized_callback &&cb) {
|
||||||
@ -123,16 +123,16 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized_callback &operator=(std::function<void(P...)> func) {
|
synchronized_callback &operator=(std::function<void(Args...)> func) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
callback = std::move(func);
|
callback = std::move(func);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(P... args) const {
|
void operator()(Args... args) const {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(args...);
|
callback(std::move(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
@ -140,8 +140,12 @@ public:
|
|||||||
return callback ? true : false;
|
return callback ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::function<void(Args...)> wrap() const {
|
||||||
|
return [this](Args... args) { (*this)(std::move(args)...); };
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(P...)> callback;
|
std::function<void(Args...)> callback;
|
||||||
mutable std::recursive_mutex mutex;
|
mutable std::recursive_mutex mutex;
|
||||||
};
|
};
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -129,7 +129,7 @@ public:
|
|||||||
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<IceTransport> initIceTransport(Description::Role role);
|
std::shared_ptr<IceTransport> initIceTransport();
|
||||||
std::shared_ptr<DtlsTransport> initDtlsTransport();
|
std::shared_ptr<DtlsTransport> initDtlsTransport();
|
||||||
std::shared_ptr<SctpTransport> initSctpTransport();
|
std::shared_ptr<SctpTransport> initSctpTransport();
|
||||||
void closeTransports();
|
void closeTransports();
|
||||||
|
@ -57,6 +57,7 @@ public:
|
|||||||
|
|
||||||
inline uint8_t version() const { return _first >> 6; }
|
inline uint8_t version() const { return _first >> 6; }
|
||||||
inline bool padding() const { return (_first >> 5) & 0x01; }
|
inline bool padding() const { return (_first >> 5) & 0x01; }
|
||||||
|
inline bool extension() const { return (_first >> 4) & 0x01; }
|
||||||
inline uint8_t csrcCount() const { return _first & 0x0F; }
|
inline uint8_t csrcCount() const { return _first & 0x0F; }
|
||||||
inline uint8_t marker() const { return _payloadType & 0b10000000; }
|
inline uint8_t marker() const { return _payloadType & 0b10000000; }
|
||||||
inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
|
inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
|
||||||
@ -77,6 +78,17 @@ public:
|
|||||||
inline void setSsrc(uint32_t ssrc) { _ssrc = htonl(ssrc); }
|
inline void setSsrc(uint32_t ssrc) { _ssrc = htonl(ssrc); }
|
||||||
|
|
||||||
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
|
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
|
||||||
|
|
||||||
|
void log() {
|
||||||
|
PLOG_VERBOSE << "RTP V: " << (int) version()
|
||||||
|
<< " P: " << (padding() ? "P" : " ")
|
||||||
|
<< " X: " << (extension() ? "X" : " ")
|
||||||
|
<< " CC: " << (int) csrcCount()
|
||||||
|
<< " M: " << (marker() ? "M" : " ")
|
||||||
|
<< " PT: " << (int) payloadType()
|
||||||
|
<< " SEQNO: " << seqNumber()
|
||||||
|
<< " TS: " << timestamp();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RTCP_ReportBlock {
|
struct RTCP_ReportBlock {
|
||||||
|
@ -106,7 +106,7 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
mFingerprint->begin(),
|
mFingerprint->begin(),
|
||||||
[](char c) { return char(std::toupper(c)); });
|
[](char c) { return char(std::toupper(c)); });
|
||||||
} else {
|
} else {
|
||||||
PLOG_WARNING << "Unknown SDP fingerprint type: " << value;
|
PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
|
||||||
}
|
}
|
||||||
} else if (key == "ice-ufrag") {
|
} else if (key == "ice-ufrag") {
|
||||||
mIceUfrag = value;
|
mIceUfrag = value;
|
||||||
|
@ -46,11 +46,12 @@ using std::chrono::system_clock;
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback)
|
gathering_state_callback gatheringStateChangeCallback)
|
||||||
: Transport(nullptr, std::move(stateChangeCallback)), mRole(role), mMid("0"),
|
: Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
|
||||||
mGatheringState(GatheringState::New), mCandidateCallback(std::move(candidateCallback)),
|
mMid("0"), mGatheringState(GatheringState::New),
|
||||||
|
mCandidateCallback(std::move(candidateCallback)),
|
||||||
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
||||||
mAgent(nullptr, nullptr) {
|
mAgent(nullptr, nullptr) {
|
||||||
|
|
||||||
@ -139,13 +140,19 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
|
|||||||
if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
|
if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
|
||||||
throw std::runtime_error("Failed to generate local SDP");
|
throw std::runtime_error("Failed to generate local SDP");
|
||||||
|
|
||||||
|
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||||
|
// setup:actpass.
|
||||||
|
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||||
return Description(string(sdp), type,
|
return Description(string(sdp), type,
|
||||||
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::setRemoteDescription(const Description &description) {
|
void IceTransport::setRemoteDescription(const Description &description) {
|
||||||
|
if (mRole == Description::Role::ActPass)
|
||||||
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
||||||
: Description::Role::Active;
|
: Description::Role::Active;
|
||||||
|
if (mRole == description.role())
|
||||||
|
throw std::logic_error("Incompatible roles with remote description");
|
||||||
|
|
||||||
mMid = description.bundleMid();
|
mMid = description.bundleMid();
|
||||||
if (juice_set_remote_description(mAgent.get(),
|
if (juice_set_remote_description(mAgent.get(),
|
||||||
@ -316,11 +323,12 @@ void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback)
|
gathering_state_callback gatheringStateChangeCallback)
|
||||||
: Transport(nullptr, std::move(stateChangeCallback)), mRole(role), mMid("0"),
|
: Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
|
||||||
mGatheringState(GatheringState::New), mCandidateCallback(std::move(candidateCallback)),
|
mMid("0"), mGatheringState(GatheringState::New),
|
||||||
|
mCandidateCallback(std::move(candidateCallback)),
|
||||||
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
||||||
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr) {
|
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr) {
|
||||||
|
|
||||||
@ -526,12 +534,21 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
|
|||||||
|
|
||||||
std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
|
std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
|
||||||
g_free);
|
g_free);
|
||||||
return Description(string(sdp.get()), type, mRole);
|
|
||||||
|
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||||
|
// setup:actpass.
|
||||||
|
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||||
|
return Description(string(sdp.get()), type,
|
||||||
|
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::setRemoteDescription(const Description &description) {
|
void IceTransport::setRemoteDescription(const Description &description) {
|
||||||
|
if (mRole == Description::Role::ActPass)
|
||||||
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
||||||
: Description::Role::Active;
|
: Description::Role::Active;
|
||||||
|
if (mRole == description.role())
|
||||||
|
throw std::logic_error("Incompatible roles with remote description");
|
||||||
|
|
||||||
mMid = description.bundleMid();
|
mMid = description.bundleMid();
|
||||||
mTrickleTimeout = !description.ended() ? 30s : 0s;
|
mTrickleTimeout = !description.ended() ? 30s : 0s;
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ public:
|
|||||||
using candidate_callback = std::function<void(const Candidate &candidate)>;
|
using candidate_callback = std::function<void(const Candidate &candidate)>;
|
||||||
using gathering_state_callback = std::function<void(GatheringState state)>;
|
using gathering_state_callback = std::function<void(GatheringState state)>;
|
||||||
|
|
||||||
IceTransport(const Configuration &config, Description::Role role,
|
IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback);
|
gathering_state_callback gatheringStateChangeCallback);
|
||||||
~IceTransport();
|
~IceTransport();
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ void PeerConnection::close() {
|
|||||||
mNegotiationNeeded = false;
|
mNegotiationNeeded = false;
|
||||||
|
|
||||||
// Close data channels asynchronously
|
// Close data channels asynchronously
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::closeDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::closeDataChannels, this);
|
||||||
|
|
||||||
closeTransports();
|
closeTransports();
|
||||||
}
|
}
|
||||||
@ -187,13 +187,7 @@ void PeerConnection::setLocalDescription(Description::Type type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = initIceTransport();
|
||||||
if (!iceTransport) {
|
|
||||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
|
||||||
// setup:actpass.
|
|
||||||
// See https://tools.ietf.org/html/rfc5763#section-5
|
|
||||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
|
||||||
}
|
|
||||||
|
|
||||||
Description localDescription = iceTransport->getLocalDescription(type);
|
Description localDescription = iceTransport->getLocalDescription(type);
|
||||||
processLocalDescription(std::move(localDescription));
|
processLocalDescription(std::move(localDescription));
|
||||||
@ -273,9 +267,7 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
auto remoteCandidates = description.extractCandidates();
|
auto remoteCandidates = description.extractCandidates();
|
||||||
auto type = description.type();
|
auto type = description.type();
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = initIceTransport();
|
||||||
if (!iceTransport)
|
|
||||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
|
||||||
|
|
||||||
iceTransport->setRemoteDescription(description);
|
iceTransport->setRemoteDescription(description);
|
||||||
processRemoteDescription(std::move(description));
|
processRemoteDescription(std::move(description));
|
||||||
@ -405,14 +397,14 @@ void PeerConnection::onTrack(std::function<void(std::shared_ptr<Track>)> callbac
|
|||||||
mTrackCallback = callback;
|
mTrackCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role) {
|
shared_ptr<IceTransport> PeerConnection::initIceTransport() {
|
||||||
PLOG_VERBOSE << "Starting ICE transport";
|
PLOG_VERBOSE << "Starting ICE transport";
|
||||||
try {
|
try {
|
||||||
if (auto transport = std::atomic_load(&mIceTransport))
|
if (auto transport = std::atomic_load(&mIceTransport))
|
||||||
return transport;
|
return transport;
|
||||||
|
|
||||||
auto transport = std::make_shared<IceTransport>(
|
auto transport = std::make_shared<IceTransport>(
|
||||||
mConfig, role, weak_bind(&PeerConnection::processLocalCandidate, this, _1),
|
mConfig, weak_bind(&PeerConnection::processLocalCandidate, this, _1),
|
||||||
[this, weak_this = weak_from_this()](IceTransport::State state) {
|
[this, weak_this = weak_from_this()](IceTransport::State state) {
|
||||||
auto shared_this = weak_this.lock();
|
auto shared_this = weak_this.lock();
|
||||||
if (!shared_this)
|
if (!shared_this)
|
||||||
@ -490,7 +482,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
|||||||
else
|
else
|
||||||
changeState(State::Connected);
|
changeState(State::Connected);
|
||||||
|
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openTracks, this));
|
mProcessor->enqueue(&PeerConnection::openTracks, this);
|
||||||
break;
|
break;
|
||||||
case DtlsTransport::State::Failed:
|
case DtlsTransport::State::Failed:
|
||||||
changeState(State::Failed);
|
changeState(State::Failed);
|
||||||
@ -561,16 +553,16 @@ shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case SctpTransport::State::Connected:
|
case SctpTransport::State::Connected:
|
||||||
changeState(State::Connected);
|
changeState(State::Connected);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::openDataChannels, this);
|
||||||
break;
|
break;
|
||||||
case SctpTransport::State::Failed:
|
case SctpTransport::State::Failed:
|
||||||
LOG_WARNING << "SCTP transport failed";
|
LOG_WARNING << "SCTP transport failed";
|
||||||
changeState(State::Failed);
|
changeState(State::Failed);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::remoteCloseDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
|
||||||
break;
|
break;
|
||||||
case SctpTransport::State::Disconnected:
|
case SctpTransport::State::Disconnected:
|
||||||
changeState(State::Disconnected);
|
changeState(State::Disconnected);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::remoteCloseDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore
|
// Ignore
|
||||||
@ -1069,19 +1061,17 @@ void PeerConnection::processLocalDescription(Description description) {
|
|||||||
mCurrentLocalDescription.emplace(std::move(*mLocalDescription));
|
mCurrentLocalDescription.emplace(std::move(*mLocalDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalDescription.emplace(std::move(description));
|
mLocalDescription.emplace(description);
|
||||||
mLocalDescription->addCandidates(std::move(existingCandidates));
|
mLocalDescription->addCandidates(std::move(existingCandidates));
|
||||||
}
|
}
|
||||||
|
|
||||||
mProcessor->enqueue([this, description = *mLocalDescription]() {
|
|
||||||
PLOG_VERBOSE << "Issuing local description: " << description;
|
PLOG_VERBOSE << "Issuing local description: " << description;
|
||||||
mLocalDescriptionCallback(std::move(description));
|
mProcessor->enqueue(mLocalDescriptionCallback.wrap(), std::move(description));
|
||||||
});
|
|
||||||
|
|
||||||
// Reciprocated tracks might need to be open
|
// Reciprocated tracks might need to be open
|
||||||
if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
||||||
dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
|
dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openTracks, this));
|
mProcessor->enqueue(&PeerConnection::openTracks, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::processLocalCandidate(Candidate candidate) {
|
void PeerConnection::processLocalCandidate(Candidate candidate) {
|
||||||
@ -1092,10 +1082,8 @@ void PeerConnection::processLocalCandidate(Candidate candidate) {
|
|||||||
candidate.resolve(Candidate::ResolveMode::Simple); // for proper SDP generation later
|
candidate.resolve(Candidate::ResolveMode::Simple); // for proper SDP generation later
|
||||||
mLocalDescription->addCandidate(candidate);
|
mLocalDescription->addCandidate(candidate);
|
||||||
|
|
||||||
mProcessor->enqueue([this, candidate = std::move(candidate)]() {
|
|
||||||
PLOG_VERBOSE << "Issuing local candidate: " << candidate;
|
PLOG_VERBOSE << "Issuing local candidate: " << candidate;
|
||||||
mLocalCandidateCallback(std::move(candidate));
|
mProcessor->enqueue(mLocalCandidateCallback.wrap(), std::move(candidate));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::processRemoteDescription(Description description) {
|
void PeerConnection::processRemoteDescription(Description description) {
|
||||||
@ -1150,12 +1138,11 @@ void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
|
|||||||
if (!dataChannel)
|
if (!dataChannel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mProcessor->enqueue(
|
mProcessor->enqueue(mDataChannelCallback.wrap(), std::move(dataChannel));
|
||||||
[this, dataChannel = std::move(dataChannel)]() { mDataChannelCallback(dataChannel); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::triggerTrack(std::shared_ptr<Track> track) {
|
void PeerConnection::triggerTrack(std::shared_ptr<Track> track) {
|
||||||
mProcessor->enqueue([this, track = std::move(track)]() { mTrackCallback(track); });
|
mProcessor->enqueue(mTrackCallback.wrap(), std::move(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnection::changeState(State state) {
|
bool PeerConnection::changeState(State state) {
|
||||||
@ -1177,7 +1164,7 @@ bool PeerConnection::changeState(State state) {
|
|||||||
// This is the last state change, so we may steal the callback
|
// This is the last state change, so we may steal the callback
|
||||||
mProcessor->enqueue([cb = std::move(mStateChangeCallback)]() { cb(State::Closed); });
|
mProcessor->enqueue([cb = std::move(mStateChangeCallback)]() { cb(State::Closed); });
|
||||||
else
|
else
|
||||||
mProcessor->enqueue([this, state]() { mStateChangeCallback(state); });
|
mProcessor->enqueue(mStateChangeCallback.wrap(), state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1189,7 +1176,7 @@ bool PeerConnection::changeGatheringState(GatheringState state) {
|
|||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << state;
|
s << state;
|
||||||
PLOG_INFO << "Changed gathering state to " << s.str();
|
PLOG_INFO << "Changed gathering state to " << s.str();
|
||||||
mProcessor->enqueue([this, state] { mGatheringStateChangeCallback(state); });
|
mProcessor->enqueue(mGatheringStateChangeCallback.wrap(), state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1200,7 +1187,7 @@ bool PeerConnection::changeSignalingState(SignalingState state) {
|
|||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << state;
|
s << state;
|
||||||
PLOG_INFO << "Changed signaling state to " << s.str();
|
PLOG_INFO << "Changed signaling state to " << s.str();
|
||||||
mProcessor->enqueue([this, state] { mSignalingStateChangeCallback(state); });
|
mProcessor->enqueue(mSignalingStateChangeCallback.wrap(), state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
|
Processor::Processor(size_t limit) : mTasks(limit) {}
|
||||||
|
|
||||||
Processor::~Processor() { join(); }
|
Processor::~Processor() { join(); }
|
||||||
|
|
||||||
void Processor::join() {
|
void Processor::join() {
|
||||||
@ -29,15 +31,13 @@ void Processor::join() {
|
|||||||
|
|
||||||
void Processor::schedule() {
|
void Processor::schedule() {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
if (mTasks.empty()) {
|
if (auto next = mTasks.tryPop()) {
|
||||||
|
ThreadPool::Instance().enqueue(std::move(*next));
|
||||||
|
} else {
|
||||||
// No more tasks
|
// No more tasks
|
||||||
mPending = false;
|
mPending = false;
|
||||||
mCondition.notify_all();
|
mCondition.notify_all();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::Instance().enqueue(std::move(mTasks.front()));
|
|
||||||
mTasks.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
#include "init.hpp"
|
#include "init.hpp"
|
||||||
#include "threadpool.hpp"
|
#include "threadpool.hpp"
|
||||||
|
#include "queue.hpp"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <future>
|
#include <future>
|
||||||
@ -34,7 +35,7 @@ namespace rtc {
|
|||||||
// Processed tasks in order by delegating them to the thread pool
|
// Processed tasks in order by delegating them to the thread pool
|
||||||
class Processor final {
|
class Processor final {
|
||||||
public:
|
public:
|
||||||
Processor() = default;
|
Processor(size_t limit = 0);
|
||||||
~Processor();
|
~Processor();
|
||||||
|
|
||||||
Processor(const Processor &) = delete;
|
Processor(const Processor &) = delete;
|
||||||
@ -52,7 +53,7 @@ protected:
|
|||||||
// Keep an init token
|
// Keep an init token
|
||||||
const init_token mInitToken = Init::Token();
|
const init_token mInitToken = Init::Token();
|
||||||
|
|
||||||
std::queue<std::function<void()>> mTasks;
|
Queue<std::function<void()>> mTasks;
|
||||||
bool mPending = false; // true iff a task is pending in the thread pool
|
bool mPending = false; // true iff a task is pending in the thread pool
|
||||||
|
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
@ -71,7 +72,7 @@ template <class F, class... Args> void Processor::enqueue(F &&f, Args &&... args
|
|||||||
ThreadPool::Instance().enqueue(std::move(task));
|
ThreadPool::Instance().enqueue(std::move(task));
|
||||||
mPending = true;
|
mPending = true;
|
||||||
} else {
|
} else {
|
||||||
mTasks.emplace(std::move(task));
|
mTasks.push(std::move(task));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,11 +100,12 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
|||||||
Instances.insert(this);
|
Instances.insert(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &SctpTransport::RecvCallback,
|
mSock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, nullptr, nullptr, 0, nullptr);
|
||||||
&SctpTransport::SendCallback, 0, this);
|
|
||||||
if (!mSock)
|
if (!mSock)
|
||||||
throw std::runtime_error("Could not create SCTP socket, errno=" + std::to_string(errno));
|
throw std::runtime_error("Could not create SCTP socket, errno=" + std::to_string(errno));
|
||||||
|
|
||||||
|
usrsctp_set_upcall(mSock, &SctpTransport::UpcallCallback, this);
|
||||||
|
|
||||||
if (usrsctp_set_non_blocking(mSock, 1))
|
if (usrsctp_set_non_blocking(mSock, 1))
|
||||||
throw std::runtime_error("Unable to set non-blocking mode, errno=" + std::to_string(errno));
|
throw std::runtime_error("Unable to set non-blocking mode, errno=" + std::to_string(errno));
|
||||||
|
|
||||||
@ -122,6 +123,10 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
|||||||
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)))
|
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)))
|
||||||
throw std::runtime_error("Could not set socket option SCTP_ENABLE_STREAM_RESET, errno=" +
|
throw std::runtime_error("Could not set socket option SCTP_ENABLE_STREAM_RESET, errno=" +
|
||||||
std::to_string(errno));
|
std::to_string(errno));
|
||||||
|
int on = 1;
|
||||||
|
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(on)))
|
||||||
|
throw std::runtime_error("Could set socket option SCTP_RECVRCVINFO, errno=" +
|
||||||
|
std::to_string(errno));
|
||||||
|
|
||||||
struct sctp_event se = {};
|
struct sctp_event se = {};
|
||||||
se.se_assoc_id = SCTP_ALL_ASSOC;
|
se.se_assoc_id = SCTP_ALL_ASSOC;
|
||||||
@ -225,6 +230,7 @@ bool SctpTransport::stop() {
|
|||||||
|
|
||||||
void SctpTransport::close() {
|
void SctpTransport::close() {
|
||||||
if (mSock) {
|
if (mSock) {
|
||||||
|
mProcessor.join();
|
||||||
usrsctp_close(mSock);
|
usrsctp_close(mSock);
|
||||||
mSock = nullptr;
|
mSock = nullptr;
|
||||||
}
|
}
|
||||||
@ -319,6 +325,59 @@ void SctpTransport::incoming(message_ptr message) {
|
|||||||
usrsctp_conninput(this, message->data(), message->size(), 0);
|
usrsctp_conninput(this, message->data(), message->size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SctpTransport::doRecv() {
|
||||||
|
std::lock_guard lock(mRecvMutex);
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const size_t bufferSize = 65536;
|
||||||
|
byte buffer[bufferSize];
|
||||||
|
socklen_t fromlen = 0;
|
||||||
|
struct sctp_rcvinfo info = {};
|
||||||
|
socklen_t infolen = sizeof(info);
|
||||||
|
unsigned int infotype = 0;
|
||||||
|
int flags = 0;
|
||||||
|
ssize_t len = usrsctp_recvv(mSock, buffer, bufferSize, nullptr, &fromlen, &info,
|
||||||
|
&infolen, &infotype, &flags);
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ECONNRESET)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("SCTP recv failed, errno=" + std::to_string(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
PLOG_VERBOSE << "SCTP recv, len=" << len;
|
||||||
|
|
||||||
|
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
||||||
|
// therefore partial notifications and messages need to be handled separately.
|
||||||
|
if (flags & MSG_NOTIFICATION) {
|
||||||
|
// SCTP event notification
|
||||||
|
mPartialNotification.insert(mPartialNotification.end(), buffer, buffer + len);
|
||||||
|
if (flags & MSG_EOR) {
|
||||||
|
// Notification is complete, process it
|
||||||
|
auto notification =
|
||||||
|
reinterpret_cast<union sctp_notification *>(mPartialNotification.data());
|
||||||
|
processNotification(notification, mPartialNotification.size());
|
||||||
|
mPartialNotification.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SCTP message
|
||||||
|
mPartialMessage.insert(mPartialMessage.end(), buffer, buffer + len);
|
||||||
|
if (flags & MSG_EOR) {
|
||||||
|
// Message is complete, process it
|
||||||
|
if (infotype != SCTP_RECVV_RCVINFO)
|
||||||
|
throw std::runtime_error("Missing SCTP recv info");
|
||||||
|
|
||||||
|
processData(std::move(mPartialMessage), info.rcv_sid,
|
||||||
|
PayloadId(ntohl(info.rcv_ppid)));
|
||||||
|
mPartialMessage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
PLOG_WARNING << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SctpTransport::trySendQueue() {
|
bool SctpTransport::trySendQueue() {
|
||||||
// Requires mSendMutex to be locked
|
// Requires mSendMutex to be locked
|
||||||
while (auto next = mSendQueue.peek()) {
|
while (auto next = mSendQueue.peek()) {
|
||||||
@ -472,44 +531,19 @@ bool SctpTransport::safeFlush() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*addr*/,
|
void SctpTransport::handleUpcall() {
|
||||||
const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
|
if(!mSock)
|
||||||
try {
|
return;
|
||||||
PLOG_VERBOSE << "Handle recv, len=" << len;
|
|
||||||
|
|
||||||
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
PLOG_VERBOSE << "Handle upcall";
|
||||||
// therefore partial notifications and messages need to be handled separately.
|
|
||||||
if (flags & MSG_NOTIFICATION) {
|
|
||||||
// SCTP event notification
|
|
||||||
mPartialNotification.insert(mPartialNotification.end(), data, data + len);
|
|
||||||
if (flags & MSG_EOR) {
|
|
||||||
// Notification is complete, process it
|
|
||||||
processNotification(
|
|
||||||
reinterpret_cast<const union sctp_notification *>(mPartialNotification.data()),
|
|
||||||
mPartialNotification.size());
|
|
||||||
mPartialNotification.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SCTP message
|
|
||||||
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
|
|
||||||
if (flags & MSG_EOR) {
|
|
||||||
// Message is complete, process it
|
|
||||||
processData(std::move(mPartialMessage), info.rcv_sid,
|
|
||||||
PayloadId(ntohl(info.rcv_ppid)));
|
|
||||||
mPartialMessage.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (const std::exception &e) {
|
int events = usrsctp_get_events(mSock);
|
||||||
PLOG_ERROR << "SCTP recv: " << e.what();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0; // success
|
|
||||||
}
|
|
||||||
|
|
||||||
int SctpTransport::handleSend(size_t free) {
|
if (events & SCTP_EVENT_READ)
|
||||||
PLOG_VERBOSE << "Handle send, free=" << free;
|
mProcessor.enqueue(&SctpTransport::doRecv, this);
|
||||||
return safeFlush() ? 0 : -1;
|
|
||||||
|
if (events & SCTP_EVENT_WRITE)
|
||||||
|
mProcessor.enqueue(&SctpTransport::safeFlush, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
|
int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
|
||||||
@ -699,31 +733,14 @@ std::optional<milliseconds> SctpTransport::rtt() {
|
|||||||
return milliseconds(status.sstat_primary.spinfo_srtt);
|
return milliseconds(status.sstat_primary.spinfo_srtt);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,
|
void SctpTransport::UpcallCallback(struct socket *, void *arg, int /* flags */) {
|
||||||
size_t len, struct sctp_rcvinfo recv_info, int flags,
|
auto *transport = static_cast<SctpTransport *>(arg);
|
||||||
void *ulp_info) {
|
|
||||||
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
|
||||||
if (Instances.find(transport) == Instances.end()) {
|
|
||||||
free(data);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret =
|
|
||||||
transport->handleRecv(sock, addr, static_cast<const byte *>(data), len, recv_info, flags);
|
|
||||||
free(data);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SctpTransport::SendCallback(struct socket *, uint32_t sb_free, void *ulp_info) {
|
|
||||||
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
std::shared_lock lock(InstancesMutex);
|
||||||
if (Instances.find(transport) == Instances.end())
|
if (Instances.find(transport) == Instances.end())
|
||||||
return -1;
|
return;
|
||||||
|
|
||||||
return transport->handleSend(size_t(sb_free));
|
transport->handleUpcall();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
|
int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
|
||||||
|
@ -21,8 +21,10 @@
|
|||||||
|
|
||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
#include "peerconnection.hpp"
|
#include "peerconnection.hpp"
|
||||||
|
#include "processor.hpp"
|
||||||
#include "queue.hpp"
|
#include "queue.hpp"
|
||||||
#include "transport.hpp"
|
#include "transport.hpp"
|
||||||
|
#include "processor.hpp"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -35,7 +37,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class SctpTransport : public Transport {
|
class SctpTransport final : public Transport {
|
||||||
public:
|
public:
|
||||||
static void Init();
|
static void Init();
|
||||||
static void Cleanup();
|
static void Cleanup();
|
||||||
@ -76,15 +78,14 @@ private:
|
|||||||
void close();
|
void close();
|
||||||
void incoming(message_ptr message) override;
|
void incoming(message_ptr message) override;
|
||||||
|
|
||||||
|
void doRecv();
|
||||||
bool trySendQueue();
|
bool trySendQueue();
|
||||||
bool trySendMessage(message_ptr message);
|
bool trySendMessage(message_ptr message);
|
||||||
void updateBufferedAmount(uint16_t streamId, long delta);
|
void updateBufferedAmount(uint16_t streamId, long delta);
|
||||||
void sendReset(uint16_t streamId);
|
void sendReset(uint16_t streamId);
|
||||||
bool safeFlush();
|
bool safeFlush();
|
||||||
|
|
||||||
int handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, size_t len,
|
void handleUpcall();
|
||||||
struct sctp_rcvinfo recv_info, int flags);
|
|
||||||
int handleSend(size_t free);
|
|
||||||
int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
|
int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
|
||||||
|
|
||||||
void processData(binary &&data, uint16_t streamId, PayloadId ppid);
|
void processData(binary &&data, uint16_t streamId, PayloadId ppid);
|
||||||
@ -93,7 +94,8 @@ private:
|
|||||||
const uint16_t mPort;
|
const uint16_t mPort;
|
||||||
struct socket *mSock;
|
struct socket *mSock;
|
||||||
|
|
||||||
std::mutex mSendMutex;
|
Processor mProcessor;
|
||||||
|
std::mutex mRecvMutex, mSendMutex;
|
||||||
Queue<message_ptr> mSendQueue;
|
Queue<message_ptr> mSendQueue;
|
||||||
std::map<uint16_t, size_t> mBufferedAmount;
|
std::map<uint16_t, size_t> mBufferedAmount;
|
||||||
amount_callback mBufferedAmountCallback;
|
amount_callback mBufferedAmountCallback;
|
||||||
@ -109,9 +111,7 @@ private:
|
|||||||
// Stats
|
// Stats
|
||||||
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
||||||
|
|
||||||
static int RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data, size_t len,
|
static void UpcallCallback(struct socket *sock, void *arg, int flags);
|
||||||
struct sctp_rcvinfo recv_info, int flags, void *ulp_info);
|
|
||||||
static int SendCallback(struct socket *sock, uint32_t sb_free, void *ulp_info);
|
|
||||||
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
||||||
|
|
||||||
static std::unordered_set<SctpTransport *> Instances;
|
static std::unordered_set<SctpTransport *> Instances;
|
||||||
|
Reference in New Issue
Block a user