mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 23:25:33 +00:00
1287 lines
35 KiB
C++
1287 lines
35 KiB
C++
/**
|
|
* Copyright (c) 2019-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 "rtc.h"
|
|
#include "rtc.hpp"
|
|
|
|
#include "impl/internals.hpp"
|
|
|
|
#include <chrono>
|
|
#include <exception>
|
|
#include <mutex>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
using namespace rtc;
|
|
using std::chrono::milliseconds;
|
|
|
|
namespace {
|
|
|
|
std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
|
|
std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
|
|
std::unordered_map<int, shared_ptr<Track>> trackMap;
|
|
#if RTC_ENABLE_MEDIA
|
|
std::unordered_map<int, shared_ptr<MediaChainableHandler>> rtcpChainableHandlerMap;
|
|
std::unordered_map<int, shared_ptr<RtcpSrReporter>> rtcpSrReporterMap;
|
|
std::unordered_map<int, shared_ptr<RtpPacketizationConfig>> rtpConfigMap;
|
|
#endif
|
|
#if RTC_ENABLE_WEBSOCKET
|
|
std::unordered_map<int, shared_ptr<WebSocket>> webSocketMap;
|
|
#endif
|
|
std::unordered_map<int, void *> userPointerMap;
|
|
std::mutex mutex;
|
|
int lastId = 0;
|
|
|
|
optional<void *> getUserPointer(int id) {
|
|
std::lock_guard lock(mutex);
|
|
auto it = userPointerMap.find(id);
|
|
return it != userPointerMap.end() ? std::make_optional(it->second) : nullopt;
|
|
}
|
|
|
|
void setUserPointer(int i, void *ptr) {
|
|
std::lock_guard lock(mutex);
|
|
userPointerMap[i] = ptr;
|
|
}
|
|
|
|
shared_ptr<PeerConnection> getPeerConnection(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = peerConnectionMap.find(id); it != peerConnectionMap.end())
|
|
return it->second;
|
|
else
|
|
throw std::invalid_argument("PeerConnection ID does not exist");
|
|
}
|
|
|
|
shared_ptr<DataChannel> getDataChannel(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = dataChannelMap.find(id); it != dataChannelMap.end())
|
|
return it->second;
|
|
else
|
|
throw std::invalid_argument("DataChannel ID does not exist");
|
|
}
|
|
|
|
shared_ptr<Track> getTrack(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = trackMap.find(id); it != trackMap.end())
|
|
return it->second;
|
|
else
|
|
throw std::invalid_argument("Track ID does not exist");
|
|
}
|
|
|
|
int emplacePeerConnection(shared_ptr<PeerConnection> ptr) {
|
|
std::lock_guard lock(mutex);
|
|
int pc = ++lastId;
|
|
peerConnectionMap.emplace(std::make_pair(pc, ptr));
|
|
userPointerMap.emplace(std::make_pair(pc, nullptr));
|
|
return pc;
|
|
}
|
|
|
|
int emplaceDataChannel(shared_ptr<DataChannel> ptr) {
|
|
std::lock_guard lock(mutex);
|
|
int dc = ++lastId;
|
|
dataChannelMap.emplace(std::make_pair(dc, ptr));
|
|
userPointerMap.emplace(std::make_pair(dc, nullptr));
|
|
return dc;
|
|
}
|
|
|
|
int emplaceTrack(shared_ptr<Track> ptr) {
|
|
std::lock_guard lock(mutex);
|
|
int tr = ++lastId;
|
|
trackMap.emplace(std::make_pair(tr, ptr));
|
|
userPointerMap.emplace(std::make_pair(tr, nullptr));
|
|
return tr;
|
|
}
|
|
|
|
void erasePeerConnection(int pc) {
|
|
std::lock_guard lock(mutex);
|
|
if (peerConnectionMap.erase(pc) == 0)
|
|
throw std::invalid_argument("Peer Connection ID does not exist");
|
|
userPointerMap.erase(pc);
|
|
}
|
|
|
|
void eraseDataChannel(int dc) {
|
|
std::lock_guard lock(mutex);
|
|
if (dataChannelMap.erase(dc) == 0)
|
|
throw std::invalid_argument("Data Channel ID does not exist");
|
|
userPointerMap.erase(dc);
|
|
}
|
|
|
|
void eraseTrack(int tr) {
|
|
std::lock_guard lock(mutex);
|
|
if (trackMap.erase(tr) == 0)
|
|
throw std::invalid_argument("Track ID does not exist");
|
|
#if RTC_ENABLE_MEDIA
|
|
rtcpSrReporterMap.erase(tr);
|
|
rtcpChainableHandlerMap.erase(tr);
|
|
rtpConfigMap.erase(tr);
|
|
#endif
|
|
userPointerMap.erase(tr);
|
|
}
|
|
|
|
#if RTC_ENABLE_MEDIA
|
|
|
|
shared_ptr<RtcpSrReporter> getRtcpSrReporter(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = rtcpSrReporterMap.find(id); it != rtcpSrReporterMap.end()) {
|
|
return it->second;
|
|
} else {
|
|
throw std::invalid_argument("RTCP SR reporter ID does not exist");
|
|
}
|
|
}
|
|
|
|
void emplaceRtcpSrReporter(shared_ptr<RtcpSrReporter> ptr, int tr) {
|
|
std::lock_guard lock(mutex);
|
|
rtcpSrReporterMap.emplace(std::make_pair(tr, ptr));
|
|
}
|
|
|
|
shared_ptr<MediaChainableHandler> getMediaChainableHandler(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) {
|
|
return it->second;
|
|
} else {
|
|
throw std::invalid_argument("RTCP chainable handler ID does not exist");
|
|
}
|
|
}
|
|
|
|
void emplaceMediaChainableHandler(shared_ptr<MediaChainableHandler> ptr, int tr) {
|
|
std::lock_guard lock(mutex);
|
|
rtcpChainableHandlerMap.emplace(std::make_pair(tr, ptr));
|
|
}
|
|
|
|
shared_ptr<RtpPacketizationConfig> getRtpConfig(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = rtpConfigMap.find(id); it != rtpConfigMap.end()) {
|
|
return it->second;
|
|
} else {
|
|
throw std::invalid_argument("RTP configuration ID does not exist");
|
|
}
|
|
}
|
|
|
|
void emplaceRtpConfig(shared_ptr<RtpPacketizationConfig> ptr, int tr) {
|
|
std::lock_guard lock(mutex);
|
|
rtpConfigMap.emplace(std::make_pair(tr, ptr));
|
|
}
|
|
|
|
shared_ptr<RtpPacketizationConfig>
|
|
createRtpPacketizationConfig(const rtcPacketizationHandlerInit *init) {
|
|
if (!init)
|
|
throw std::invalid_argument("Unexpected null pointer for packetization handler init");
|
|
|
|
if (!init->cname)
|
|
throw std::invalid_argument("Unexpected null pointer for cname");
|
|
|
|
return std::make_shared<RtpPacketizationConfig>(init->ssrc, init->cname, init->payloadType,
|
|
init->clockRate, init->sequenceNumber,
|
|
init->timestamp);
|
|
}
|
|
|
|
#endif // RTC_ENABLE_MEDIA
|
|
|
|
#if RTC_ENABLE_WEBSOCKET
|
|
shared_ptr<WebSocket> getWebSocket(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = webSocketMap.find(id); it != webSocketMap.end())
|
|
return it->second;
|
|
else
|
|
throw std::invalid_argument("WebSocket ID does not exist");
|
|
}
|
|
|
|
int emplaceWebSocket(shared_ptr<WebSocket> ptr) {
|
|
std::lock_guard lock(mutex);
|
|
int ws = ++lastId;
|
|
webSocketMap.emplace(std::make_pair(ws, ptr));
|
|
userPointerMap.emplace(std::make_pair(ws, nullptr));
|
|
return ws;
|
|
}
|
|
|
|
void eraseWebSocket(int ws) {
|
|
std::lock_guard lock(mutex);
|
|
if (webSocketMap.erase(ws) == 0)
|
|
throw std::invalid_argument("WebSocket ID does not exist");
|
|
userPointerMap.erase(ws);
|
|
}
|
|
#endif
|
|
|
|
shared_ptr<Channel> getChannel(int id) {
|
|
std::lock_guard lock(mutex);
|
|
if (auto it = dataChannelMap.find(id); it != dataChannelMap.end())
|
|
return it->second;
|
|
if (auto it = trackMap.find(id); it != trackMap.end())
|
|
return it->second;
|
|
#if RTC_ENABLE_WEBSOCKET
|
|
if (auto it = webSocketMap.find(id); it != webSocketMap.end())
|
|
return it->second;
|
|
#endif
|
|
throw std::invalid_argument("DataChannel, Track, or WebSocket ID does not exist");
|
|
}
|
|
|
|
template <typename F> int wrap(F func) {
|
|
try {
|
|
return int(func());
|
|
|
|
} catch (const std::invalid_argument &e) {
|
|
PLOG_ERROR << e.what();
|
|
return RTC_ERR_INVALID;
|
|
} catch (const std::exception &e) {
|
|
PLOG_ERROR << e.what();
|
|
return RTC_ERR_FAILURE;
|
|
}
|
|
}
|
|
|
|
int copyAndReturn(string s, char *buffer, int size) {
|
|
if (!buffer)
|
|
return int(s.size() + 1);
|
|
|
|
if (size < int(s.size()))
|
|
return RTC_ERR_TOO_SMALL;
|
|
|
|
std::copy(s.begin(), s.end(), buffer);
|
|
buffer[s.size()] = '\0';
|
|
return int(s.size() + 1);
|
|
}
|
|
|
|
int copyAndReturn(binary b, char *buffer, int size) {
|
|
if (!buffer)
|
|
return int(b.size());
|
|
|
|
if (size < int(b.size()))
|
|
return RTC_ERR_TOO_SMALL;
|
|
|
|
auto data = reinterpret_cast<const char *>(b.data());
|
|
std::copy(data, data + b.size(), buffer);
|
|
buffer[b.size()] = '\0';
|
|
return int(b.size());
|
|
}
|
|
|
|
template<typename T>
|
|
int copyAndReturn(std::vector<T> b, T *buffer, int size) {
|
|
if (!buffer)
|
|
return int(b.size());
|
|
|
|
if (size < int(b.size()))
|
|
return RTC_ERR_TOO_SMALL;
|
|
memcpy(buffer, b.data(), b.size() * sizeof(*buffer));
|
|
return int(b.size());
|
|
}
|
|
|
|
#if RTC_ENABLE_MEDIA
|
|
// function is used in RTC_ENABLE_MEDIA only
|
|
string lowercased(string str) {
|
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
return str;
|
|
}
|
|
#endif // RTC_ENABLE_MEDIA
|
|
} // namespace
|
|
|
|
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
|
|
LogCallback callback = nullptr;
|
|
if (cb)
|
|
callback = [cb](LogLevel level, string message) {
|
|
cb(static_cast<rtcLogLevel>(level), message.c_str());
|
|
};
|
|
|
|
InitLogger(static_cast<LogLevel>(level), callback);
|
|
}
|
|
|
|
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
|
|
|
|
void *rtcGetUserPointer(int i) { return getUserPointer(i).value_or(nullptr); }
|
|
|
|
int rtcCreatePeerConnection(const rtcConfiguration *config) {
|
|
return wrap([config] {
|
|
Configuration c;
|
|
for (int i = 0; i < config->iceServersCount; ++i)
|
|
c.iceServers.emplace_back(string(config->iceServers[i]));
|
|
|
|
if (config->bindAddress)
|
|
c.bindAddress = string(config->bindAddress);
|
|
|
|
if (config->portRangeBegin > 0 || config->portRangeEnd > 0) {
|
|
c.portRangeBegin = config->portRangeBegin;
|
|
c.portRangeEnd = config->portRangeEnd;
|
|
}
|
|
|
|
c.certificateType = static_cast<CertificateType>(config->certificateType);
|
|
c.enableIceTcp = config->enableIceTcp;
|
|
c.disableAutoNegotiation = config->disableAutoNegotiation;
|
|
|
|
if (config->mtu > 0)
|
|
c.mtu = size_t(config->mtu);
|
|
|
|
if (config->maxMessageSize)
|
|
c.maxMessageSize = size_t(config->maxMessageSize);
|
|
|
|
return emplacePeerConnection(std::make_shared<PeerConnection>(c));
|
|
});
|
|
}
|
|
|
|
int rtcDeletePeerConnection(int pc) {
|
|
return wrap([pc] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
peerConnection->onDataChannel(nullptr);
|
|
peerConnection->onTrack(nullptr);
|
|
peerConnection->onLocalDescription(nullptr);
|
|
peerConnection->onLocalCandidate(nullptr);
|
|
peerConnection->onStateChange(nullptr);
|
|
peerConnection->onGatheringStateChange(nullptr);
|
|
|
|
erasePeerConnection(pc);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcCreateDataChannel(int pc, const char *label) {
|
|
return rtcCreateDataChannelEx(pc, label, nullptr);
|
|
}
|
|
|
|
int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
|
|
return wrap([&] {
|
|
DataChannelInit dci = {};
|
|
if (init) {
|
|
auto *reliability = &init->reliability;
|
|
dci.reliability.unordered = reliability->unordered;
|
|
if (reliability->unreliable) {
|
|
if (reliability->maxPacketLifeTime > 0) {
|
|
dci.reliability.type = Reliability::Type::Timed;
|
|
dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
|
|
} else {
|
|
dci.reliability.type = Reliability::Type::Rexmit;
|
|
dci.reliability.rexmit = reliability->maxRetransmits;
|
|
}
|
|
} else {
|
|
dci.reliability.type = Reliability::Type::Reliable;
|
|
}
|
|
|
|
dci.negotiated = init->negotiated;
|
|
dci.id = init->manualStream ? std::make_optional(init->stream) : nullopt;
|
|
dci.protocol = init->protocol ? init->protocol : "";
|
|
}
|
|
|
|
auto peerConnection = getPeerConnection(pc);
|
|
int dc = emplaceDataChannel(
|
|
peerConnection->createDataChannel(string(label ? label : ""), std::move(dci)));
|
|
|
|
if (auto ptr = getUserPointer(pc))
|
|
rtcSetUserPointer(dc, *ptr);
|
|
|
|
return dc;
|
|
});
|
|
}
|
|
|
|
int rtcIsOpen(int cid) {
|
|
return wrap([cid] {
|
|
return getChannel(cid)->isOpen();
|
|
});
|
|
}
|
|
|
|
int rtcDeleteDataChannel(int dc) {
|
|
return wrap([dc] {
|
|
auto dataChannel = getDataChannel(dc);
|
|
dataChannel->onOpen(nullptr);
|
|
dataChannel->onClosed(nullptr);
|
|
dataChannel->onError(nullptr);
|
|
dataChannel->onMessage(nullptr);
|
|
dataChannel->onBufferedAmountLow(nullptr);
|
|
dataChannel->onAvailable(nullptr);
|
|
|
|
eraseDataChannel(dc);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
|
|
return wrap([&] {
|
|
if (!mediaDescriptionSdp)
|
|
throw std::invalid_argument("Unexpected null pointer for track media description");
|
|
|
|
auto peerConnection = getPeerConnection(pc);
|
|
Description::Media media{string(mediaDescriptionSdp)};
|
|
int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
|
|
if (auto ptr = getUserPointer(pc))
|
|
rtcSetUserPointer(tr, *ptr);
|
|
|
|
return tr;
|
|
});
|
|
}
|
|
|
|
int rtcAddTrackEx(int pc, const rtcTrackInit *init) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (!init)
|
|
throw std::invalid_argument("Unexpected null pointer for track init");
|
|
|
|
auto direction = static_cast<Description::Direction>(init->direction);
|
|
|
|
string mid;
|
|
if (init->mid) {
|
|
mid = string(init->mid);
|
|
} else {
|
|
switch (init->codec) {
|
|
case RTC_CODEC_H264:
|
|
case RTC_CODEC_VP8:
|
|
case RTC_CODEC_VP9:
|
|
mid = "video";
|
|
break;
|
|
case RTC_CODEC_OPUS:
|
|
mid = "audio";
|
|
break;
|
|
default:
|
|
mid = "video";
|
|
break;
|
|
}
|
|
}
|
|
|
|
optional<Description::Media> optDescription = nullopt;
|
|
|
|
switch (init->codec) {
|
|
case RTC_CODEC_H264:
|
|
case RTC_CODEC_VP8:
|
|
case RTC_CODEC_VP9: {
|
|
auto desc = Description::Video(mid, direction);
|
|
switch (init->codec) {
|
|
case RTC_CODEC_H264:
|
|
desc.addH264Codec(init->payloadType);
|
|
break;
|
|
case RTC_CODEC_VP8:
|
|
desc.addVP8Codec(init->payloadType);
|
|
break;
|
|
case RTC_CODEC_VP9:
|
|
desc.addVP8Codec(init->payloadType);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
optDescription = desc;
|
|
break;
|
|
}
|
|
case RTC_CODEC_OPUS: {
|
|
auto desc = Description::Audio(mid, direction);
|
|
switch (init->codec) {
|
|
case RTC_CODEC_OPUS:
|
|
desc.addOpusCodec(init->payloadType);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
optDescription = desc;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!optDescription)
|
|
throw std::invalid_argument("Unexpected codec");
|
|
|
|
auto desc = std::move(*optDescription);
|
|
desc.addSSRC(init->ssrc, init->name ? std::make_optional(string(init->name)) : nullopt,
|
|
init->msid ? std::make_optional(string(init->msid)) : nullopt,
|
|
init->trackId ? std::make_optional(string(init->trackId)) : nullopt);
|
|
|
|
int tr = emplaceTrack(peerConnection->addTrack(std::move(desc)));
|
|
|
|
if (auto ptr = getUserPointer(pc))
|
|
rtcSetUserPointer(tr, *ptr);
|
|
|
|
return tr;
|
|
});
|
|
}
|
|
|
|
int rtcDeleteTrack(int tr) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
track->onOpen(nullptr);
|
|
track->onClosed(nullptr);
|
|
track->onError(nullptr);
|
|
track->onMessage(nullptr);
|
|
track->onBufferedAmountLow(nullptr);
|
|
track->onAvailable(nullptr);
|
|
|
|
eraseTrack(tr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetTrackDescription(int tr, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
return copyAndReturn(track->description(), buffer, size);
|
|
});
|
|
}
|
|
|
|
#if RTC_ENABLE_MEDIA
|
|
|
|
void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid, const char *_trackID) {
|
|
|
|
optional<string> name = nullopt;
|
|
if (_name) {
|
|
name = string(_name);
|
|
}
|
|
|
|
optional<string> msid = nullopt;
|
|
if (_msid) {
|
|
msid = string(_msid);
|
|
}
|
|
|
|
optional<string> trackID = nullopt;
|
|
if (_trackID) {
|
|
trackID = string(_trackID);
|
|
}
|
|
|
|
description->addSSRC(ssrc, name, msid, trackID);
|
|
}
|
|
|
|
int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
// create RTP configuration
|
|
auto rtpConfig = createRtpPacketizationConfig(init);
|
|
// create packetizer
|
|
auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
|
|
: RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE;
|
|
auto packetizer = std::make_shared<H264RtpPacketizer>(rtpConfig, maxFragmentSize);
|
|
// create H264 handler
|
|
auto h264Handler = std::make_shared<H264PacketizationHandler>(packetizer);
|
|
emplaceMediaChainableHandler(h264Handler, tr);
|
|
emplaceRtpConfig(rtpConfig, tr);
|
|
// set handler
|
|
track->setMediaHandler(h264Handler);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
// create RTP configuration
|
|
auto rtpConfig = createRtpPacketizationConfig(init);
|
|
// create packetizer
|
|
auto packetizer = std::make_shared<OpusRtpPacketizer>(rtpConfig);
|
|
// create Opus handler
|
|
auto opusHandler = std::make_shared<OpusPacketizationHandler>(packetizer);
|
|
emplaceMediaChainableHandler(opusHandler, tr);
|
|
emplaceRtpConfig(rtpConfig, tr);
|
|
// set handler
|
|
track->setMediaHandler(opusHandler);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcChainRtcpSrReporter(int tr) {
|
|
return wrap([tr] {
|
|
auto config = getRtpConfig(tr);
|
|
auto reporter = std::make_shared<RtcpSrReporter>(config);
|
|
emplaceRtcpSrReporter(reporter, tr);
|
|
auto chainableHandler = getMediaChainableHandler(tr);
|
|
chainableHandler->addToChain(reporter);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
|
|
return wrap([tr, maxStoredPacketsCount] {
|
|
auto responder = std::make_shared<RtcpNackResponder>(maxStoredPacketsCount);
|
|
auto chainableHandler = getMediaChainableHandler(tr);
|
|
chainableHandler->addToChain(responder);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetRtpConfigurationStartTime(int id, const rtcStartTime *startTime) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
auto epoch = startTime->since1970 ? RtpPacketizationConfig::EpochStart::T1970
|
|
: RtpPacketizationConfig::EpochStart::T1900;
|
|
config->setStartTime(startTime->seconds, epoch, startTime->timestamp);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcStartRtcpSenderReporterRecording(int id) {
|
|
return wrap([id] {
|
|
auto sender = getRtcpSrReporter(id);
|
|
sender->startRecording();
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
*timestamp = config->secondsToTimestamp(seconds);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
*seconds = config->timestampToSeconds(timestamp);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
*timestamp = config->timestamp;
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetTrackStartTimestamp(int id, uint32_t *timestamp) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
*timestamp = config->startTimestamp;
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp) {
|
|
return wrap([&] {
|
|
auto config = getRtpConfig(id);
|
|
config->timestamp = timestamp;
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
|
|
return wrap([&] {
|
|
auto sender = getRtcpSrReporter(id);
|
|
*timestamp = sender->previousReportedTimestamp;
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetNeedsToSendRtcpSr(int id) {
|
|
return wrap([id] {
|
|
auto sender = getRtcpSrReporter(id);
|
|
sender->setNeedsToReport();
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetTrackPayloadTypesForCodec(int tr, const char * ccodec, int * buffer, int size) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
auto codec = lowercased(string(ccodec));
|
|
auto description = track->description();
|
|
std::vector<int> payloadTypes{};
|
|
payloadTypes.reserve(std::max(size, 0));
|
|
for (auto it = description.beginMaps(); it != description.endMaps(); it++) {
|
|
auto element = *it;
|
|
if (lowercased(element.second.format) == codec) {
|
|
payloadTypes.push_back(element.first);
|
|
}
|
|
}
|
|
return copyAndReturn(payloadTypes, buffer, size);
|
|
});
|
|
}
|
|
|
|
int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int bufferSize) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
auto ssrcs = track->description().getSSRCs();
|
|
return copyAndReturn(ssrcs, buffer, bufferSize);
|
|
});
|
|
}
|
|
|
|
int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char * cname, int cnameSize) {
|
|
return wrap([&] {
|
|
auto track = getTrack(tr);
|
|
auto description = track->description();
|
|
auto optCName = description.getCNameForSsrc(ssrc);
|
|
if (optCName.has_value()) {
|
|
return copyAndReturn(optCName.value(), cname, cnameSize);
|
|
} else {
|
|
return 0;
|
|
}
|
|
});
|
|
}
|
|
|
|
int rtcGetSsrcsForType(const char * mediaType, const char * sdp, uint32_t * buffer, int bufferSize) {
|
|
return wrap([&] {
|
|
auto type = lowercased(string(mediaType));
|
|
auto oldSDP = string(sdp);
|
|
auto description = Description(oldSDP, "unspec");
|
|
auto mediaCount = description.mediaCount();
|
|
for (unsigned int i = 0; i < mediaCount; i++) {
|
|
if (std::holds_alternative<Description::Media *>(description.media(i))) {
|
|
auto media = std::get<Description::Media *>(description.media(i));
|
|
auto currentMediaType = lowercased(media->type());
|
|
if (currentMediaType == type) {
|
|
auto ssrcs = media->getSSRCs();
|
|
return copyAndReturn(ssrcs, buffer, bufferSize);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int rtcSetSsrcForType(const char * mediaType, const char * sdp, char * buffer, const int bufferSize,
|
|
rtcSsrcForTypeInit * init) {
|
|
return wrap([&] {
|
|
auto type = lowercased(string(mediaType));
|
|
auto prevSDP = string(sdp);
|
|
auto description = Description(prevSDP, "unspec");
|
|
auto mediaCount = description.mediaCount();
|
|
for (unsigned int i = 0; i < mediaCount; i++) {
|
|
if (std::holds_alternative<Description::Media *>(description.media(i))) {
|
|
auto media = std::get<Description::Media *>(description.media(i));
|
|
auto currentMediaType = lowercased(media->type());
|
|
if (currentMediaType == type) {
|
|
setSSRC(media, init->ssrc, init->name, init->msid, init->trackId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return copyAndReturn(string(description), buffer, bufferSize);
|
|
});
|
|
}
|
|
|
|
#endif // RTC_ENABLE_MEDIA
|
|
#if RTC_ENABLE_WEBSOCKET
|
|
|
|
int rtcCreateWebSocket(const char *url) {
|
|
return wrap([&] {
|
|
auto ws = std::make_shared<WebSocket>();
|
|
ws->open(url);
|
|
return emplaceWebSocket(ws);
|
|
});
|
|
}
|
|
|
|
int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config) {
|
|
return wrap([&] {
|
|
WebSocket::Configuration c;
|
|
c.disableTlsVerification = config->disableTlsVerification;
|
|
auto ws = std::make_shared<WebSocket>(c);
|
|
ws->open(url);
|
|
return emplaceWebSocket(ws);
|
|
});
|
|
}
|
|
|
|
int rtcDeleteWebsocket(int ws) {
|
|
return wrap([&] {
|
|
auto webSocket = getWebSocket(ws);
|
|
webSocket->onOpen(nullptr);
|
|
webSocket->onClosed(nullptr);
|
|
webSocket->onError(nullptr);
|
|
webSocket->onMessage(nullptr);
|
|
webSocket->onBufferedAmountLow(nullptr);
|
|
webSocket->onAvailable(nullptr);
|
|
|
|
eraseWebSocket(ws);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
#endif
|
|
|
|
int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onLocalDescription([pc, cb](Description desc) {
|
|
if (auto ptr = getUserPointer(pc))
|
|
cb(pc, string(desc).c_str(), desc.typeString().c_str(), *ptr);
|
|
});
|
|
else
|
|
peerConnection->onLocalDescription(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onLocalCandidate([pc, cb](Candidate cand) {
|
|
if (auto ptr = getUserPointer(pc))
|
|
cb(pc, cand.candidate().c_str(), cand.mid().c_str(), *ptr);
|
|
});
|
|
else
|
|
peerConnection->onLocalCandidate(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
|
|
if (auto ptr = getUserPointer(pc))
|
|
cb(pc, static_cast<rtcState>(state), *ptr);
|
|
});
|
|
else
|
|
peerConnection->onStateChange(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
|
|
if (auto ptr = getUserPointer(pc))
|
|
cb(pc, static_cast<rtcGatheringState>(state), *ptr);
|
|
});
|
|
else
|
|
peerConnection->onGatheringStateChange(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onSignalingStateChange([pc, cb](PeerConnection::SignalingState state) {
|
|
if (auto ptr = getUserPointer(pc))
|
|
cb(pc, static_cast<rtcSignalingState>(state), *ptr);
|
|
});
|
|
else
|
|
peerConnection->onGatheringStateChange(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onDataChannel([pc, cb](shared_ptr<DataChannel> dataChannel) {
|
|
int dc = emplaceDataChannel(dataChannel);
|
|
if (auto ptr = getUserPointer(pc)) {
|
|
rtcSetUserPointer(dc, *ptr);
|
|
cb(pc, dc, *ptr);
|
|
}
|
|
});
|
|
else
|
|
peerConnection->onDataChannel(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
if (cb)
|
|
peerConnection->onTrack([pc, cb](shared_ptr<Track> track) {
|
|
int tr = emplaceTrack(track);
|
|
if (auto ptr = getUserPointer(pc)) {
|
|
rtcSetUserPointer(tr, *ptr);
|
|
cb(pc, tr, *ptr);
|
|
}
|
|
});
|
|
else
|
|
peerConnection->onTrack(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetLocalDescription(int pc, const char *type) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
peerConnection->setLocalDescription(type ? Description::stringToType(type)
|
|
: Description::Type::Unspec);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (!sdp)
|
|
throw std::invalid_argument("Unexpected null pointer for remote description");
|
|
|
|
peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (!cand)
|
|
throw std::invalid_argument("Unexpected null pointer for remote candidate");
|
|
|
|
peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetLocalDescription(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto desc = peerConnection->localDescription())
|
|
return copyAndReturn(string(*desc), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetRemoteDescription(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto desc = peerConnection->remoteDescription())
|
|
return copyAndReturn(string(*desc), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetLocalDescriptionType(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto desc = peerConnection->localDescription())
|
|
return copyAndReturn(desc->typeString(), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetRemoteDescriptionType(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto desc = peerConnection->remoteDescription())
|
|
return copyAndReturn(desc->typeString(), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetLocalAddress(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto addr = peerConnection->localAddress())
|
|
return copyAndReturn(std::move(*addr), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetRemoteAddress(int pc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
if (auto addr = peerConnection->remoteAddress())
|
|
return copyAndReturn(std::move(*addr), buffer, size);
|
|
else
|
|
return RTC_ERR_NOT_AVAIL;
|
|
});
|
|
}
|
|
|
|
int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, int remoteSize) {
|
|
return wrap([&] {
|
|
auto peerConnection = getPeerConnection(pc);
|
|
|
|
Candidate localCand;
|
|
Candidate remoteCand;
|
|
if (!peerConnection->getSelectedCandidatePair(&localCand, &remoteCand))
|
|
return RTC_ERR_NOT_AVAIL;
|
|
|
|
int localRet = copyAndReturn(string(localCand), local, localSize);
|
|
if (localRet < 0)
|
|
return localRet;
|
|
|
|
int remoteRet = copyAndReturn(string(remoteCand), remote, remoteSize);
|
|
if (remoteRet < 0)
|
|
return remoteRet;
|
|
|
|
return std::max(localRet, remoteRet);
|
|
});
|
|
}
|
|
|
|
int rtcGetDataChannelStream(int dc) {
|
|
return wrap([dc] {
|
|
auto dataChannel = getDataChannel(dc);
|
|
return int(dataChannel->id());
|
|
});
|
|
}
|
|
|
|
int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto dataChannel = getDataChannel(dc);
|
|
return copyAndReturn(dataChannel->label(), buffer, size);
|
|
});
|
|
}
|
|
|
|
int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
|
|
return wrap([&] {
|
|
auto dataChannel = getDataChannel(dc);
|
|
return copyAndReturn(dataChannel->protocol(), buffer, size);
|
|
});
|
|
}
|
|
|
|
int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
|
|
return wrap([&] {
|
|
auto dataChannel = getDataChannel(dc);
|
|
|
|
if (!reliability)
|
|
throw std::invalid_argument("Unexpected null pointer for reliability");
|
|
|
|
Reliability dcr = dataChannel->reliability();
|
|
std::memset(reliability, 0, sizeof(*reliability));
|
|
reliability->unordered = dcr.unordered;
|
|
if (dcr.type == Reliability::Type::Timed) {
|
|
reliability->unreliable = true;
|
|
reliability->maxPacketLifeTime = int(std::get<milliseconds>(dcr.rexmit).count());
|
|
} else if (dcr.type == Reliability::Type::Rexmit) {
|
|
reliability->unreliable = true;
|
|
reliability->maxRetransmits = std::get<int>(dcr.rexmit);
|
|
} else {
|
|
reliability->unreliable = false;
|
|
}
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onOpen([id, cb]() {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, *ptr);
|
|
});
|
|
else
|
|
channel->onOpen(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onClosed([id, cb]() {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, *ptr);
|
|
});
|
|
else
|
|
channel->onClosed(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onError([id, cb](string error) {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, error.c_str(), *ptr);
|
|
});
|
|
else
|
|
channel->onError(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onMessage(
|
|
[id, cb](binary b) {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, reinterpret_cast<const char *>(b.data()), int(b.size()), *ptr);
|
|
},
|
|
[id, cb](string s) {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, s.c_str(), -int(s.size() + 1), *ptr);
|
|
});
|
|
else
|
|
channel->onMessage(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSendMessage(int id, const char *data, int size) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
|
|
if (!data && size != 0)
|
|
throw std::invalid_argument("Unexpected null pointer for data");
|
|
|
|
if (size >= 0) {
|
|
auto b = reinterpret_cast<const byte *>(data);
|
|
channel->send(binary(b, b + size));
|
|
return size;
|
|
} else {
|
|
string str(data);
|
|
int len = int(str.size());
|
|
channel->send(std::move(str));
|
|
return len;
|
|
}
|
|
});
|
|
}
|
|
|
|
int rtcGetBufferedAmount(int id) {
|
|
return wrap([id] {
|
|
auto channel = getChannel(id);
|
|
return int(channel->bufferedAmount());
|
|
});
|
|
}
|
|
|
|
int rtcSetBufferedAmountLowThreshold(int id, int amount) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
channel->setBufferedAmountLowThreshold(size_t(amount));
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onBufferedAmountLow([id, cb]() {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, *ptr);
|
|
});
|
|
else
|
|
channel->onBufferedAmountLow(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcGetAvailableAmount(int id) {
|
|
return wrap([id] { return int(getChannel(id)->availableAmount()); });
|
|
}
|
|
|
|
int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
if (cb)
|
|
channel->onAvailable([id, cb]() {
|
|
if (auto ptr = getUserPointer(id))
|
|
cb(id, *ptr);
|
|
});
|
|
else
|
|
channel->onAvailable(nullptr);
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|
|
|
|
int rtcReceiveMessage(int id, char *buffer, int *size) {
|
|
return wrap([&] {
|
|
auto channel = getChannel(id);
|
|
|
|
if (!size)
|
|
throw std::invalid_argument("Unexpected null pointer for size");
|
|
|
|
*size = std::abs(*size);
|
|
|
|
auto message = channel->peek();
|
|
if (!message)
|
|
return RTC_ERR_NOT_AVAIL;
|
|
|
|
return std::visit( //
|
|
overloaded{
|
|
[&](binary b) {
|
|
int ret = copyAndReturn(std::move(b), buffer, *size);
|
|
if (ret >= 0) {
|
|
channel->receive(); // discard
|
|
*size = ret;
|
|
return RTC_ERR_SUCCESS;
|
|
} else {
|
|
*size = int(b.size());
|
|
return ret;
|
|
}
|
|
},
|
|
[&](string s) {
|
|
int ret = copyAndReturn(std::move(s), buffer, *size);
|
|
if (ret >= 0) {
|
|
channel->receive(); // discard
|
|
*size = -ret;
|
|
return RTC_ERR_SUCCESS;
|
|
} else {
|
|
*size = -int(s.size() + 1);
|
|
return ret;
|
|
}
|
|
},
|
|
},
|
|
*message);
|
|
});
|
|
}
|
|
|
|
void rtcPreload() { rtc::Preload(); }
|
|
|
|
void rtcCleanup() { rtc::Cleanup(); }
|
|
|
|
int rtcSetSctpSettings(const rtcSctpSettings *settings) {
|
|
return wrap([&] {
|
|
SctpSettings s = {};
|
|
|
|
if (settings->recvBufferSize > 0)
|
|
s.recvBufferSize = size_t(settings->recvBufferSize);
|
|
|
|
if (settings->sendBufferSize > 0)
|
|
s.sendBufferSize = size_t(settings->sendBufferSize);
|
|
|
|
if (settings->maxChunksOnQueue > 0)
|
|
s.maxChunksOnQueue = size_t(settings->maxChunksOnQueue);
|
|
|
|
if (settings->initialCongestionWindow > 0)
|
|
s.initialCongestionWindow = size_t(settings->initialCongestionWindow);
|
|
|
|
if (settings->maxBurst > 0)
|
|
s.maxBurst = size_t(settings->maxBurst);
|
|
else if (settings->maxBurst < 0)
|
|
s.maxBurst = size_t(0); // setting to 0 disables, not setting chooses optimized default
|
|
|
|
if (settings->congestionControlModule >= 0)
|
|
s.congestionControlModule = unsigned(settings->congestionControlModule);
|
|
|
|
if (settings->delayedSackTimeMs > 0)
|
|
s.delayedSackTime = std::chrono::milliseconds(settings->delayedSackTimeMs);
|
|
else if (settings->delayedSackTimeMs < 0)
|
|
s.delayedSackTime = std::chrono::milliseconds(0);
|
|
|
|
if (settings->minRetransmitTimeoutMs > 0)
|
|
s.minRetransmitTimeout = std::chrono::milliseconds(settings->minRetransmitTimeoutMs);
|
|
|
|
if (settings->maxRetransmitTimeoutMs > 0)
|
|
s.maxRetransmitTimeout = std::chrono::milliseconds(settings->maxRetransmitTimeoutMs);
|
|
|
|
if (settings->initialRetransmitTimeoutMs > 0)
|
|
s.initialRetransmitTimeout = std::chrono::milliseconds(settings->initialRetransmitTimeoutMs);
|
|
|
|
if (settings->maxRetransmitAttempts > 0)
|
|
s.maxRetransmitAttempts = settings->maxRetransmitAttempts;
|
|
|
|
if (settings->heartbeatIntervalMs > 0)
|
|
s.heartbeatInterval = std::chrono::milliseconds(settings->heartbeatIntervalMs);
|
|
|
|
SetSctpSettings(std::move(s));
|
|
return RTC_ERR_SUCCESS;
|
|
});
|
|
}
|