Merge branch 'master' into feature/chainable-rtcp-handlers

# Conflicts:
#	src/h264packetizationhandler.cpp
This commit is contained in:
Filip Klembara
2021-02-08 10:19:26 +01:00
35 changed files with 260 additions and 87 deletions

2
.gitignore vendored
View File

@ -5,7 +5,7 @@ node_modules/
*.a *.a
*.so *.so
compile_commands.json compile_commands.json
tests /tests
.DS_Store .DS_Store
.idea .idea

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
project(libdatachannel project(libdatachannel
VERSION 0.11.0 VERSION 0.11.1
LANGUAGES CXX) LANGUAGES CXX)
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library") set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
@ -135,6 +135,24 @@ set(TESTS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/benchmark.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/benchmark.cpp
) )
set(TESTS_UWP_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Logo.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/package.appxManifest
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SmallLogo.png
${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)
set(BENCHMARK_UWP_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Logo.png
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/package.appxManifest
${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SmallLogo.png
${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)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
@ -292,24 +310,30 @@ endif()
# Tests # Tests
if(NOT NO_TESTS) if(NOT NO_TESTS)
add_executable(datachannel-tests ${TESTS_SOURCES}) if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
# Add resource files needed for UWP apps.
add_executable(datachannel-tests ${TESTS_SOURCES} ${TESTS_UWP_RESOURCES})
else()
add_executable(datachannel-tests ${TESTS_SOURCES})
endif()
set_target_properties(datachannel-tests PROPERTIES set_target_properties(datachannel-tests PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
CXX_STANDARD 17) CXX_STANDARD 17)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") # Prevent a bug in manifest generation for UWP set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
endif()
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-tests datachannel) target_link_libraries(datachannel-tests datachannel)
# Benchmark # Benchmark
add_executable(datachannel-benchmark test/benchmark.cpp) if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
# Add resource files needed for UWP apps.
add_executable(datachannel-benchmark test/benchmark.cpp ${BENCHMARK_UWP_RESOURCES})
else()
add_executable(datachannel-benchmark test/benchmark.cpp)
endif()
set_target_properties(datachannel-benchmark PROPERTIES set_target_properties(datachannel-benchmark PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
CXX_STANDARD 17) CXX_STANDARD 17)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") # Prevent a bug in manifest generation for UWP set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
endif()
target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1) target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-benchmark datachannel) target_link_libraries(datachannel-benchmark datachannel)

View File

@ -54,12 +54,10 @@ function createPeerConnection() {
// connect audio / video // connect audio / video
pc.addEventListener('track', function (evt) { pc.addEventListener('track', function (evt) {
if (evt.track.kind == 'video') { document.getElementById('media').style.display = 'block';
document.getElementById('media').style.display = 'block'; const videoTag = document.getElementById('video');
document.getElementById('video').srcObject = evt.streams[0]; videoTag.srcObject = evt.streams[0];
} else { videoTag.play();
document.getElementById('audio').srcObject = evt.streams[0];
}
}); });
let time_start = null; let time_start = null;

View File

@ -52,7 +52,6 @@
<div id="media" style="display: none"> <div id="media" style="display: none">
<h2>Media</h2> <h2>Media</h2>
<audio id="audio" autoplay></audio>
<video id="video" autoplay playsinline></video> <video id="video" autoplay playsinline></video>
</div> </div>

View File

@ -214,7 +214,7 @@ int main(int argc, char **argv) try {
shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) { shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
auto video = Description::Video(cname); auto video = Description::Video(cname);
video.addH264Codec(payloadType); video.addH264Codec(payloadType);
video.addSSRC(ssrc, cname, msid); video.addSSRC(ssrc, cname, msid, cname);
auto track = pc->addTrack(video); auto track = pc->addTrack(video);
// create RTP configuration // create RTP configuration
auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate)); auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate));
@ -238,7 +238,7 @@ shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const
shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) { shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
auto audio = Description::Audio(cname); auto audio = Description::Audio(cname);
audio.addOpusCodec(payloadType); audio.addOpusCodec(payloadType);
audio.addSSRC(ssrc, cname, msid); audio.addSSRC(ssrc, cname, msid, cname);
auto track = pc->addTrack(audio); auto track = pc->addTrack(audio);
// create RTP configuration // create RTP configuration
auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate)); auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate));

View File

@ -44,7 +44,7 @@ struct RTC_CPP_EXPORT IceServer {
RelayType relayType_ = RelayType::TurnUdp); RelayType relayType_ = RelayType::TurnUdp);
string hostname; string hostname;
string service; uint16_t port;
Type type; Type type;
string username; string username;
string password; string password;
@ -54,11 +54,11 @@ struct RTC_CPP_EXPORT IceServer {
struct RTC_CPP_EXPORT ProxyServer { struct RTC_CPP_EXPORT ProxyServer {
enum class Type { None = 0, Socks5, Http, Last = Http }; enum class Type { None = 0, Socks5, Http, Last = Http };
ProxyServer(Type type_, string ip_, uint16_t port_, string username_ = "", ProxyServer(Type type_, string hostname_, uint16_t port_, string username_ = "",
string password_ = ""); string password_ = "");
Type type; Type type;
string ip; string hostname;
uint16_t port; uint16_t port;
string username; string username;
string password; string password;

View File

@ -141,9 +141,9 @@ public:
void removeFormat(const string &fmt); void removeFormat(const string &fmt);
void addSSRC(uint32_t ssrc, std::optional<string> name, void addSSRC(uint32_t ssrc, std::optional<string> name,
std::optional<string> msid = nullopt); std::optional<string> msid = nullopt, std::optional<string> trackID = nullopt);
void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name, void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
std::optional<string> msid = nullopt); std::optional<string> msid = nullopt, std::optional<string> trackID = nullopt);
bool hasSSRC(uint32_t ssrc); bool hasSSRC(uint32_t ssrc);
std::vector<uint32_t> getSSRCs(); std::vector<uint32_t> getSSRCs();

View File

@ -16,8 +16,8 @@
* along with this program; If not, see <http://www.gnu.org/licenses/>. * along with this program; If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef H264RtpPacketizer_hpp #ifndef H264_RTP_PACKETIZER_H
#define H264RtpPacketizer_hpp #define H264_RTP_PACKETIZER_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
@ -64,4 +64,4 @@ private:
#endif /* RTC_ENABLE_MEDIA */ #endif /* RTC_ENABLE_MEDIA */
#endif /* H264RtpPacketizer_hpp */ #endif /* H264_RTP_PACKETIZER_H */

View File

@ -16,8 +16,8 @@
* along with this program; If not, see <http://www.gnu.org/licenses/>. * along with this program; If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef NalUnit_hpp #ifndef NAL_UNIT_H
#define NalUnit_hpp #define NAL_UNIT_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
@ -152,4 +152,4 @@ public:
#endif /* RTC_ENABLE_MEDIA */ #endif /* RTC_ENABLE_MEDIA */
#endif /* NalUnit_hpp */ #endif /* NAL_UNIT_H */

View File

@ -216,8 +216,9 @@ RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
/// @param _direction Direction /// @param _direction Direction
/// @param _name Name (optional) /// @param _name Name (optional)
/// @param _msid MSID (optional) /// @param _msid MSID (optional)
/// @param _trackID Track ID used in MSID (optional)
/// @returns Track id /// @returns Track id
RTC_EXPORT int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection direction, const char *_name, const char *_msid); RTC_EXPORT int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection direction, const char *_name, const char *_msid, const char *_trackID);
/// Set H264PacketizationHandler for track /// Set H264PacketizationHandler for track
/// @param tr Track id /// @param tr Track id

View File

@ -167,9 +167,12 @@ bool Candidate::resolve(ResolveMode mode) {
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer, if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN, MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) { NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
try {
mPort = uint16_t(std::stoul(servbuffer));
} catch (...) {
return false;
}
mAddress = nodebuffer; mAddress = nodebuffer;
mPort = uint16_t(std::stoul(servbuffer));
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4; mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort; PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
break; break;

View File

@ -453,7 +453,7 @@ int rtcDeleteDataChannel(int dc) {
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid) { void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid, const char *_trackID) {
optional<string> name = nullopt; optional<string> name = nullopt;
if (_name) { if (_name) {
@ -465,11 +465,16 @@ void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name,
msid = string(_msid); msid = string(_msid);
} }
description->addSSRC(ssrc, name, msid); optional<string> trackID = nullopt;
if (_trackID) {
trackID = string(_trackID);
}
description->addSSRC(ssrc, name, msid, trackID);
} }
int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid,
rtcDirection _direction, const char *_name, const char *_msid) { rtcDirection _direction, const char *_name, const char *_msid, const char *_trackID) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
@ -534,7 +539,7 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
throw std::invalid_argument("Unexpected codec"); throw std::invalid_argument("Unexpected codec");
} else { } else {
auto description = optDescription.value(); auto description = optDescription.value();
setSSRC(&description, ssrc, _name, _msid); setSSRC(&description, ssrc, _name, _msid, _trackID);
int tr = emplaceTrack(peerConnection->addTrack(std::move(description))); int tr = emplaceTrack(peerConnection->addTrack(std::move(description)));
if (auto ptr = getUserPointer(pc)) { if (auto ptr = getUserPointer(pc)) {

View File

@ -60,32 +60,51 @@ IceServer::IceServer(const string &url) {
username = opt[6].value_or(""); username = opt[6].value_or("");
password = opt[8].value_or(""); password = opt[8].value_or("");
hostname = opt[10].value();
service = opt[12].value_or(relayType == RelayType::TurnTls ? "5349" : "3478");
hostname = opt[10].value();
while (!hostname.empty() && hostname.front() == '[') while (!hostname.empty() && hostname.front() == '[')
hostname.erase(hostname.begin()); hostname.erase(hostname.begin());
while (!hostname.empty() && hostname.back() == ']') while (!hostname.empty() && hostname.back() == ']')
hostname.pop_back(); hostname.pop_back();
string service = opt[12].value_or(relayType == RelayType::TurnTls ? "5349" : "3478");
try {
port = uint16_t(std::stoul(service));
} catch (...) {
throw std::invalid_argument("Invalid ICE server port in URL: " + service);
}
} }
IceServer::IceServer(string hostname_, uint16_t port_) IceServer::IceServer(string hostname_, uint16_t port_)
: IceServer(std::move(hostname_), std::to_string(port_)) {} : hostname(std::move(hostname_)), port(port_), type(Type::Stun) {}
IceServer::IceServer(string hostname_, string service_) IceServer::IceServer(string hostname_, string service_)
: hostname(std::move(hostname_)), service(std::move(service_)), type(Type::Stun) {} : hostname(std::move(hostname_)), type(Type::Stun) {
try {
port = uint16_t(std::stoul(service_));
} catch (...) {
throw std::invalid_argument("Invalid ICE server port: " + service_);
}
}
IceServer::IceServer(string hostname_, uint16_t port_, string username_, string password_, IceServer::IceServer(string hostname_, uint16_t port_, string username_, string password_,
RelayType relayType_) RelayType relayType_)
: IceServer(hostname_, std::to_string(port_), std::move(username_), std::move(password_), : hostname(std::move(hostname_)), port(port_), type(Type::Turn), username(std::move(username_)),
relayType_) {} password(std::move(password_)), relayType(relayType_) {}
IceServer::IceServer(string hostname_, string service_, string username_, string password_, IceServer::IceServer(string hostname_, string service_, string username_, string password_,
RelayType relayType_) RelayType relayType_)
: hostname(std::move(hostname_)), service(std::move(service_)), type(Type::Turn), : hostname(std::move(hostname_)), type(Type::Turn), username(std::move(username_)),
username(std::move(username_)), password(std::move(password_)), relayType(relayType_) {} password(std::move(password_)), relayType(relayType_) {
try {
port = uint16_t(std::stoul(service_));
} catch (...) {
throw std::invalid_argument("Invalid ICE server port: " + service_);
}
}
ProxyServer::ProxyServer(Type type_, string ip_, uint16_t port_, string username_, string password_) ProxyServer::ProxyServer(Type type_, string hostname_, uint16_t port_, string username_,
: type(type_), ip(ip_), port(port_), username(username_), password(password_) {} string password_)
: type(type_), hostname(hostname_), port(port_), username(username_), password(password_) {}
} // namespace rtc } // namespace rtc

View File

@ -59,7 +59,13 @@ inline std::pair<string_view, string_view> parse_pair(string_view attr) {
} }
template <typename T> T to_integer(string_view s) { template <typename T> T to_integer(string_view s) {
return std::is_signed<T>::value ? T(std::stol(string(s))) : T(std::stoul(string(s))); const string str(s);
try {
return std::is_signed<T>::value ? T(std::stol(str)) : T(std::stoul(str));
}
catch(...) {
throw std::invalid_argument("Invalid integer \"" + str + "\" in description");
}
} }
} // namespace } // namespace
@ -519,20 +525,20 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
} }
void Description::Media::addSSRC(uint32_t ssrc, std::optional<string> name, void Description::Media::addSSRC(uint32_t ssrc, std::optional<string> name,
std::optional<string> msid) { std::optional<string> msid, std::optional<string> trackID) {
if (name) if (name)
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name); mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
else else
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc)); mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
if (msid) if (msid)
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + *msid); mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + trackID.value_or(*msid));
mSsrcs.emplace_back(ssrc); mSsrcs.emplace_back(ssrc);
} }
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name, void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
std::optional<string> msid) { std::optional<string> msid, std::optional<string> trackID) {
auto it = mAttributes.begin(); auto it = mAttributes.begin();
while (it != mAttributes.end()) { while (it != mAttributes.end()) {
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) { if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
@ -540,7 +546,7 @@ void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optio
} else } else
it++; it++;
} }
addSSRC(ssrc, std::move(name), std::move(msid)); addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
} }
void Description::Media::removeSSRC(uint32_t oldSSRC) { void Description::Media::removeSSRC(uint32_t oldSSRC) {
@ -823,7 +829,7 @@ void Description::Media::parseSdpLine(string_view line) {
} else if (key == "rtcp-mux") { } else if (key == "rtcp-mux") {
// always added // always added
} else if (key == "ssrc") { } else if (key == "ssrc") {
mSsrcs.emplace_back(std::stoul(string(value))); mSsrcs.emplace_back(to_integer<uint32_t>(value));
} else { } else {
Entry::parseSdpLine(line); Entry::parseSdpLine(line);
} }
@ -843,7 +849,7 @@ std::vector<uint32_t> Description::Media::getSSRCs() {
for (auto &val : mAttributes) { for (auto &val : mAttributes) {
PLOG_DEBUG << val; PLOG_DEBUG << val;
if (val.find("ssrc:") == 0) { if (val.find("ssrc:") == 0) {
vec.emplace_back(std::stoul(string(val.substr(5, val.find(" "))))); vec.emplace_back(to_integer<uint32_t>(val.substr(5, val.find(" "))));
} }
} }
return vec; return vec;

View File

@ -108,7 +108,7 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
break; break;
} }
} }
index++;
unsigned long long naluStartIndex = index; unsigned long long naluStartIndex = index;
while (index < message->size()) { while (index < message->size()) {

View File

@ -103,11 +103,11 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
// Pick a STUN server // Pick a STUN server
for (auto &server : servers) { for (auto &server : servers) {
if (!server.hostname.empty() && server.type == IceServer::Type::Stun) { if (!server.hostname.empty() && server.type == IceServer::Type::Stun) {
if (server.service.empty()) if (server.port == 0)
server.service = "3478"; // STUN UDP port server.port = 3478; // STUN UDP port
PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.service << "\""; PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port << "\"";
jconfig.stun_server_host = server.hostname.c_str(); jconfig.stun_server_host = server.hostname.c_str();
jconfig.stun_server_port = uint16_t(std::stoul(server.service)); jconfig.stun_server_port = server.port;
break; break;
} }
} }
@ -119,13 +119,13 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
int k = 0; int k = 0;
for (auto &server : servers) { for (auto &server : servers) {
if (!server.hostname.empty() && server.type == IceServer::Type::Turn) { if (!server.hostname.empty() && server.type == IceServer::Type::Turn) {
if (server.service.empty()) if (server.port == 0)
server.service = "3478"; // TURN UDP port server.port = 3478; // TURN UDP port
PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.service << "\""; PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\"";
turn_servers[k].host = server.hostname.c_str(); turn_servers[k].host = server.hostname.c_str();
turn_servers[k].username = server.username.c_str(); turn_servers[k].username = server.username.c_str();
turn_servers[k].password = server.password.c_str(); turn_servers[k].password = server.password.c_str();
turn_servers[k].port = uint16_t(std::stoul(server.service)); turn_servers[k].port = server.port;
if (++k >= MAX_TURN_SERVERS_COUNT) if (++k >= MAX_TURN_SERVERS_COUNT)
break; break;
} }
@ -402,7 +402,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
if (config.proxyServer.has_value()) { if (config.proxyServer.has_value()) {
ProxyServer proxyServer = config.proxyServer.value(); ProxyServer proxyServer = config.proxyServer.value();
g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-type", proxyServer.type, nullptr); g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-type", proxyServer.type, nullptr);
g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-ip", proxyServer.ip.c_str(), nullptr); g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-ip", proxyServer.hostname.c_str(), nullptr);
g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-port", proxyServer.port, nullptr); g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-port", proxyServer.port, nullptr);
g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-username", proxyServer.username.c_str(), g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-username", proxyServer.username.c_str(),
nullptr); nullptr);
@ -422,8 +422,8 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
continue; continue;
if (server.type != IceServer::Type::Stun) if (server.type != IceServer::Type::Stun)
continue; continue;
if (server.service.empty()) if (server.port == 0)
server.service = "3478"; // STUN UDP port server.port = 3478; // STUN UDP port
struct addrinfo hints = {}; struct addrinfo hints = {};
hints.ai_family = AF_INET; // IPv4 hints.ai_family = AF_INET; // IPv4
@ -431,9 +431,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
hints.ai_protocol = IPPROTO_UDP; hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) { if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
&result) != 0) {
PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':' PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
<< server.service; << server.port;
continue; continue;
} }
@ -444,7 +445,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
servbuffer, MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICNODE_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) { NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.service PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port
<< "\""; << "\"";
g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr); g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port", g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port",
@ -466,8 +467,8 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
continue; continue;
if (server.type != IceServer::Type::Turn) if (server.type != IceServer::Type::Turn)
continue; continue;
if (server.service.empty()) if (server.port == 0)
server.service = server.relayType == IceServer::RelayType::TurnTls ? "5349" : "3478"; server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478;
struct addrinfo hints = {}; struct addrinfo hints = {};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
@ -477,9 +478,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) { if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
&result) != 0) {
PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':' PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
<< server.service; << server.port;
continue; continue;
} }
@ -490,7 +492,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
servbuffer, MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICNODE_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) { NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.service PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port
<< "\""; << "\"";
NiceRelayType niceRelayType; NiceRelayType niceRelayType;
switch (server.relayType) { switch (server.relayType) {

View File

@ -25,18 +25,39 @@ Processor::Processor(size_t limit) : mTasks(limit) {}
Processor::~Processor() { join(); } Processor::~Processor() { join(); }
void Processor::join() { void Processor::join() {
std::unique_lock lock(mMutex); // We need to detect situations where the thread pool does not execute a pending task at exit
mCondition.wait(lock, [this]() { return !mPending && mTasks.empty(); }); std::optional<unsigned int> counter;
while (true) {
std::shared_future<void> pending;
{
std::unique_lock lock(mMutex);
if (!mPending // no pending task
|| (counter && *counter == mCounter)) { // or no scheduled task after the last one
// Processing is stopped, clear everything and return
mPending.reset();
while (!mTasks.empty())
mTasks.pop();
return;
}
pending = *mPending;
counter = mCounter;
}
// Wait for the pending task
pending.wait();
}
} }
void Processor::schedule() { void Processor::schedule() {
std::unique_lock lock(mMutex); std::unique_lock lock(mMutex);
if (auto next = mTasks.tryPop()) { if (auto next = mTasks.tryPop()) {
ThreadPool::Instance().enqueue(std::move(*next)); mPending = ThreadPool::Instance().enqueue(std::move(*next)).share();
++mCounter;
} else { } else {
// No more tasks mPending.reset(); // No more tasks
mPending = false;
mCondition.notify_all();
} }
} }

View File

@ -54,10 +54,10 @@ protected:
const init_token mInitToken = Init::Token(); const init_token mInitToken = Init::Token();
Queue<std::function<void()>> mTasks; Queue<std::function<void()>> mTasks;
bool mPending = false; // true iff a task is pending in the thread pool std::optional<std::shared_future<void>> mPending; // future of the pending task
unsigned int mCounter = 0; // Number of scheduled tasks
mutable std::mutex mMutex; mutable std::mutex mMutex;
std::condition_variable mCondition;
}; };
template <class F, class... Args> void Processor::enqueue(F &&f, Args &&...args) { template <class F, class... Args> void Processor::enqueue(F &&f, Args &&...args) {
@ -65,12 +65,12 @@ template <class F, class... Args> void Processor::enqueue(F &&f, Args &&...args)
auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...); auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
auto task = [this, bound = std::move(bound)]() mutable { auto task = [this, bound = std::move(bound)]() mutable {
scope_guard guard(std::bind(&Processor::schedule, this)); // chain the next task scope_guard guard(std::bind(&Processor::schedule, this)); // chain the next task
return bound(); bound();
}; };
if (!mPending) { if (!mPending) {
ThreadPool::Instance().enqueue(std::move(task)); mPending = ThreadPool::Instance().enqueue(std::move(task)).share();
mPending = true; ++mCounter;
} else { } else {
mTasks.push(std::move(task)); mTasks.push(std::move(task));
} }

View File

@ -588,7 +588,6 @@ int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t
std::unique_lock lock(mWriteMutex); std::unique_lock lock(mWriteMutex);
PLOG_VERBOSE << "Handle write, len=" << len; PLOG_VERBOSE << "Handle write, len=" << len;
auto message = make_message(data, data + len);
if (!outgoing(make_message(data, data + len))) if (!outgoing(make_message(data, data + len)))
return -1; return -1;

View File

@ -96,6 +96,10 @@ std::function<void()> ThreadPool::dequeue() {
mCondition.wait(lock); mCondition.wait(lock);
} }
} }
while (!mTasks.empty())
mTasks.pop();
return nullptr; return nullptr;
} }

View File

@ -100,8 +100,16 @@ auto ThreadPool::schedule(clock::duration delay, F &&f, Args &&...args)
template <class F, class... Args> template <class F, class... Args>
auto ThreadPool::schedule(clock::time_point time, F &&f, Args &&...args) auto ThreadPool::schedule(clock::time_point time, F &&f, Args &&...args)
-> invoke_future_t<F, Args...> { -> invoke_future_t<F, Args...> {
std::unique_lock lock(mMutex);
using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>; using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
std::unique_lock lock(mMutex);
if (mJoining) {
std::promise<R> promise;
std::future<R> result = promise.get_future();
promise.set_exception(std::make_exception_ptr(
std::runtime_error("Scheduled a task while joining the thread pool")));
return result;
}
auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...); auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
auto task = std::make_shared<std::packaged_task<R()>>([bound = std::move(bound)]() mutable { auto task = std::make_shared<std::packaged_task<R()>>([bound = std::move(bound)]() mutable {
try { try {

BIN
test/uwp/benchmark/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
IgnorableNamespaces="uap mp">
<Identity Name="EF284012-D73C-360F-A800-2D2910675E38" Publisher="CN=CMake" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="EF284012-D73C-360F-A800-2D2910675E38" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>datachannel-benchmark</DisplayName>
<PublisherDisplayName>CMake</PublisherDisplayName>
<Logo>StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application
Id="App"
Executable="benchmark.exe"
EntryPoint="datachannel-benchmark.App"
desktop4:Subsystem="console"
desktop4:SupportsMultipleInstances="true"
iot2:Subsystem="console"
iot2:SupportsMultipleInstances="true">
<uap:VisualElements
DisplayName="datachannel-benchmark"
Description="datachannel-benchmark"
BackgroundColor="#336699"
Square150x150Logo="Logo.png"
Square44x44Logo="SmallLogo44x44.png">
<uap:SplashScreen Image="SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
</Package>

BIN
test/uwp/tests/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
IgnorableNamespaces="uap mp">
<Identity Name="7D85E652-0312-3746-8F5A-315F52839776" Publisher="CN=CMake" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="7D85E652-0312-3746-8F5A-315F52839776" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>datachannel-tests</DisplayName>
<PublisherDisplayName>CMake</PublisherDisplayName>
<Logo>StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application
Id="App"
Executable="tests.exe"
EntryPoint="datachannel-tests.App"
desktop4:Subsystem="console"
desktop4:SupportsMultipleInstances="true"
iot2:Subsystem="console"
iot2:SupportsMultipleInstances="true">
<uap:VisualElements
DisplayName="datachannel-tests"
Description="datachannel-tests"
BackgroundColor="#336699"
Square150x150Logo="Logo.png"
Square44x44Logo="SmallLogo44x44.png">
<uap:SplashScreen Image="SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
</Package>