浏览代码

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

# Conflicts:
#	src/h264packetizationhandler.cpp
Filip Klembara 4 年之前
父节点
当前提交
36a88c605a

+ 1 - 1
.gitignore

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

+ 33 - 9
CMakeLists.txt

@@ -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)
-	endif()
+	set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
 	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)
-	endif()
+	set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
 	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)

+ 4 - 6
examples/streamer/client.js

@@ -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('video').srcObject = evt.streams[0];
-        } else {
-            document.getElementById('audio').srcObject = evt.streams[0];
-        }
+        document.getElementById('media').style.display = 'block';
+        const videoTag = document.getElementById('video');
+        videoTag.srcObject = evt.streams[0];
+        videoTag.play();
     });
     });
 
 
     let time_start = null;
     let time_start = null;

+ 0 - 1
examples/streamer/index.html

@@ -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>
 
 

+ 2 - 2
examples/streamer/main.cpp

@@ -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));

+ 3 - 3
include/rtc/configuration.hpp

@@ -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;

+ 2 - 2
include/rtc/description.hpp

@@ -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();
 
 

+ 3 - 3
include/rtc/h264rtppacketizer.hpp

@@ -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
-#define H264RtpPacketizer_hpp
+#ifndef H264_RTP_PACKETIZER_H
+#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 */

+ 3 - 3
include/rtc/nalunit.hpp

@@ -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
-#define NalUnit_hpp
+#ifndef NAL_UNIT_H
+#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 */

+ 2 - 1
include/rtc/rtc.h

@@ -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

+ 5 - 2
src/candidate.cpp

@@ -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;

+ 9 - 4
src/capi.cpp

@@ -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)) {

+ 29 - 10
src/configuration.cpp

@@ -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_),
-                relayType_) {}
+    : hostname(std::move(hostname_)), port(port_), type(Type::Turn), username(std::move(username_)),
+      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),
-      username(std::move(username_)), password(std::move(password_)), relayType(relayType_) {}
+    : hostname(std::move(hostname_)), type(Type::Turn), username(std::move(username_)),
+      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_)
-    : type(type_), ip(ip_), port(port_), username(username_), password(password_) {}
+ProxyServer::ProxyServer(Type type_, string hostname_, uint16_t port_, string username_,
+                         string password_)
+    : type(type_), hostname(hostname_), port(port_), username(username_), password(password_) {}
 
 
 } // namespace rtc
 } // namespace rtc

+ 13 - 7
src/description.cpp

@@ -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;

+ 1 - 1
src/h264rtppacketizer.cpp

@@ -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()) {

+ 21 - 19
src/icetransport.cpp

@@ -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())
-				server.service = "3478"; // STUN UDP port
-			PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.service << "\"";
+			if (server.port == 0)
+				server.port = 3478; // STUN UDP port
+			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())
-				server.service = "3478"; // TURN UDP port
-			PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.service << "\"";
+			if (server.port == 0)
+				server.port = 3478; // TURN UDP port
+			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())
-			server.service = "3478"; // STUN UDP port
+		if (server.port == 0)
+			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())
-			server.service = server.relayType == IceServer::RelayType::TurnTls ? "5349" : "3478";
+		if (server.port == 0)
+			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) {

+ 27 - 6
src/processor.cpp

@@ -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);
-	mCondition.wait(lock, [this]() { return !mPending && mTasks.empty(); });
+	// We need to detect situations where the thread pool does not execute a pending task at exit
+	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 = false;
-		mCondition.notify_all();
+		mPending.reset(); // No more tasks
 	}
 	}
 }
 }
 
 

+ 5 - 5
src/processor.hpp

@@ -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 = true;
+		mPending = ThreadPool::Instance().enqueue(std::move(task)).share();
+		++mCounter;
 	} else {
 	} else {
 		mTasks.push(std::move(task));
 		mTasks.push(std::move(task));
 	}
 	}

+ 0 - 1
src/sctptransport.cpp

@@ -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;
 
 

+ 4 - 0
src/threadpool.cpp

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

+ 9 - 1
src/threadpool.hpp

@@ -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 {

二进制
test/uwp/benchmark/Logo.png


二进制
test/uwp/benchmark/SmallLogo.png


二进制
test/uwp/benchmark/SmallLogo44x44.png


二进制
test/uwp/benchmark/SplashScreen.png


二进制
test/uwp/benchmark/StoreLogo.png


二进制
test/uwp/benchmark/Windows_TemporaryKey.pfx


+ 42 - 0
test/uwp/benchmark/package.appxManifest

@@ -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>

二进制
test/uwp/tests/Logo.png


二进制
test/uwp/tests/SmallLogo.png


二进制
test/uwp/tests/SmallLogo44x44.png


二进制
test/uwp/tests/SplashScreen.png


二进制
test/uwp/tests/StoreLogo.png


二进制
test/uwp/tests/Windows_TemporaryKey.pfx


+ 42 - 0
test/uwp/tests/package.appxManifest

@@ -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>