Compare commits

...

69 Commits

Author SHA1 Message Date
9457492169 Bumped version to 0.13.0 2021-05-07 21:30:04 +02:00
384c84e8aa Merge branch 'v0.12' 2021-05-07 21:26:17 +02:00
e4ab5273fc Bumped version to 0.12.3 2021-05-07 21:24:17 +02:00
b315869989 Bumped libjuice to v0.7.2 2021-04-30 22:30:34 +02:00
aea6708d27 Merge pull request #416 from paullouisageneau/cleanup-api
Refactor C media API
2021-04-29 17:12:37 +02:00
2166386d42 Fixed comments 2021-04-29 16:07:04 +02:00
f88394ab75 Merge pull request #418 from paullouisageneau/cleanup-has-description
Remove declarations of hasLocalDescription() and hasRemoteDescription()
2021-04-29 16:04:28 +02:00
ba7e0e2a35 Removed declarations of hasLocalDescription() and hasRemoteDescription() 2021-04-29 15:24:08 +02:00
fba965b46c Fixed compilation without media 2021-04-28 21:36:36 +02:00
eff0faf6e1 Refactored C media API 2021-04-27 22:58:46 +02:00
cd68d1dba7 Use make_shared where possible 2021-04-26 22:34:06 +02:00
ce9fa374d0 Merge pull request #414 from paullouisageneau/sctp-max-burst
Add SCTP max burst setting
2021-04-26 21:31:47 +02:00
2bf03205cf Added entry for sfu-media 2021-04-24 15:01:04 +02:00
07a5591cda Added comments for SCTP settings 2021-04-20 19:28:54 +02:00
282ca48b12 Added max burst setting for SCTP 2021-04-20 19:28:54 +02:00
bf0b3ce1b9 Resolve use of deprecated ::result_type typedef.
Also adds #include <utility> since this file names std::move.

```
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(std::weak_ptr<_Ty>)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(rtc::Candidate)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<bool (__cdecl rtc::impl::PeerConnection::* )(const std::string &) const>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(rtc::message_ptr)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(uint16_t,size_t)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
```
2021-04-20 09:20:56 +02:00
48a275f830 Merge pull request #413 from BillyONeal/warning_fix
Resolve use of deprecated ::result_type typedef.
2021-04-20 09:11:56 +02:00
a15b2ad468 Resolve use of deprecated ::result_type typedef.
Also adds #include <utility> since this file names std::move.

```
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(std::weak_ptr<_Ty>)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(rtc::Candidate)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<bool (__cdecl rtc::impl::PeerConnection::* )(const std::string &) const>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(rtc::message_ptr)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
         D:\buildtrees\libdatachannel\src\v0.12.0-2d418b9dcd.clean\include\rtc\utils.hpp(35,23): error C4996: 'std::_Weak_result_type<std::_Is_memfunptr<void (__cdecl rtc::impl::PeerConnection::* )(uint16_t,size_t)>,void>::result_type': warning STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. You can define _SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. [D:\buildtrees\libdatachannel\x64-uwp-dbg\datachannel.vcxproj]
```
2021-04-19 14:41:53 -07:00
afd4639d2d Merge pull request #410 from paullouisageneau/cmake-improvements
CMake improvements
2021-04-19 18:50:18 +02:00
653ec35ba8 Merge pull request #411 from paullouisageneau/dependabot/npm_and_yarn/examples/signaling-server-nodejs/websocket-1.0.34
Bump websocket from 1.0.33 to 1.0.34 in /examples/signaling-server-nodejs
2021-04-19 09:33:32 +02:00
2d344f1186 Bump websocket in /examples/signaling-server-nodejs
Bumps [websocket](https://github.com/theturtle32/WebSocket-Node) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/theturtle32/WebSocket-Node/releases)
- [Changelog](https://github.com/theturtle32/WebSocket-Node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/theturtle32/WebSocket-Node/compare/v1.0.33...v1.0.34)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-19 07:13:42 +00:00
13f3624aee CMake improvements 2021-04-18 09:53:59 +02:00
ebe64ed8ef Merge pull request #408 from paullouisageneau/open-cb-recv-dc
Call DataChannel::onOpen() callback on receiving side
2021-04-15 22:59:03 +02:00
ae429410fe Introduced synchronized_stored_callback to support callbacks set after they are triggered 2021-04-15 22:21:24 +02:00
bbbc8ca4e6 Introduced onOpen() callback in client example 2021-04-15 09:38:00 +02:00
5346d31cc1 Introduced onOpen() callback on receiving side in tests 2021-04-15 09:21:56 +02:00
d86de619dc Enforce proper callback order between open and message 2021-04-15 09:21:56 +02:00
d62166e1a7 Trigger open callback after data channel or track callback 2021-04-15 09:21:56 +02:00
b353fbe44d Refactored flushing of pending DataChannels and Tracks 2021-04-15 09:21:56 +02:00
079926443c Merge branch 'v0.12' 2021-04-15 09:21:35 +02:00
2ed6845201 Merge pull request #404 from paullouisageneau/plog-private
Make plog a private dependency
2021-04-14 21:42:17 +02:00
655175d21e Bumped version to 0.12.2 2021-04-14 12:24:12 +02:00
c85943e916 support finding openssl form homebrew on M1 Macs
homebrew changed its default installation paths for M1 Macs
2021-04-14 12:22:26 +02:00
6aee0ff12c Merge pull request #407 from arvidn/homebrew-m1-openssl
support finding openssl from homebrew on M1 Macs
2021-04-14 12:21:19 +02:00
6cea78c618 support finding openssl form homebrew on M1 Macs
homebrew changed its default installation paths for M1 Macs
2021-04-14 07:54:01 +02:00
c929be592b Fixed struct timeval undeclared on Windows 2021-04-13 23:03:16 +02:00
0c882d074e Marked packetizers and RTCP handlers final 2021-04-13 23:03:16 +02:00
4ca78db146 Reorganized stuff to make plog private 2021-04-13 23:03:16 +02:00
2fd3da482a Merge pull request #406 from paullouisageneau/v0.12
Make impl::DataChannel destructor virtual
2021-04-13 23:02:06 +02:00
9bc9eea5b4 Merge pull request #405 from paullouisageneau/fix-license-headers
Fixed example license headers in library
2021-04-13 22:59:22 +02:00
d7261dd0ac Fixed example license headers in library 2021-04-13 22:57:48 +02:00
1cc7910bf1 Made impl::DataChannel destructor virtual 2021-04-13 22:29:25 +02:00
436d77eab7 Merge pull request #400 from paullouisageneau/sctp-settings
Add SCTP settings API
2021-04-13 21:51:06 +02:00
93b598a78c Merge pull request #402 from murat-dogan/simple-fix
Simple fix
2021-04-13 21:49:51 +02:00
e4019ebec8 define macMessegeSize 2021-04-13 15:00:25 +03:00
89c0a7918a delete unneeded module 2021-04-13 14:41:51 +03:00
6b5d888db3 Fixed recv instead of send for buffer size 2021-04-13 09:45:42 +02:00
0fddf50245 Reorganized includes to hide Init implementation 2021-04-12 22:12:10 +02:00
251bfc7def Added SCTP settings API and enhanced integer conversions in SctpTransport 2021-04-12 19:11:53 +02:00
dd4949e78e Merge pull request #398 from murat-dogan/master
Add Multiple Data Channel Option
2021-04-11 21:07:41 +02:00
0d0e3ae0ff use at method of unordered_map 2021-04-11 16:51:44 +03:00
b3a980f079 Change unordered_map write logic 2021-04-11 13:22:48 +03:00
ab946bb711 Multiple Data Channel Output Example 2021-04-10 19:58:48 +03:00
120e57cc5c fix app outputs 2021-04-10 19:55:59 +03:00
f1080bfd0f implement dataChannelCount option 2021-04-10 19:48:26 +03:00
8190188a05 add dataChannelCount option 2021-04-10 19:48:10 +03:00
a48f080605 Merge branch 'v0.12' 2021-04-10 16:25:46 +02:00
62b435a4aa Fixed compilation warnings 2021-04-10 16:22:05 +02:00
9da756bd12 Updated Jamfile 2021-04-10 10:54:10 +02:00
1acfcaa830 Added missing include in Jamfile 2021-04-10 09:38:01 +02:00
f5b584f536 Handle empty RTCIceCandidate.candidate as "end of candidates" indicator 2021-04-08 15:10:26 +02:00
3eb8ab567e Bumped version to 0.12.1 2021-04-07 17:45:03 +02:00
f1a7afb28e Merge pull request #392 from paullouisageneau/fix-htonll
Fix htonll fallback implementation
2021-04-07 16:01:53 +02:00
9a7b672d6e Fixed htonll fallback implementation 2021-04-07 15:50:11 +02:00
9da58f4343 Updated Readme 2021-04-06 19:02:33 +02:00
ef2e7c609a Merge pull request #390 from paullouisageneau/fix-dc-double-offer
Fix DataChannel stream number assignment on double offer
2021-04-06 18:52:44 +02:00
d330738b41 Fixed synchronization in DataChannel::shiftStream() 2021-04-06 18:44:29 +02:00
791e6b1e32 Fixed DataChannel shifting on double offer 2021-04-06 18:39:12 +02:00
b5884c84fc Fixed mistakes in Doc 2021-04-04 17:07:17 +02:00
88 changed files with 2497 additions and 1747 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
project(libdatachannel project(libdatachannel
VERSION 0.12.0 VERSION 0.13.0
LANGUAGES CXX) LANGUAGES CXX)
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library") set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
@ -43,8 +43,7 @@ set(LIBDATACHANNEL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/init.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/log.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/message.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/message.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/peerconnection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/peerconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpreceivingsession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpreceivingsession.cpp
@ -62,6 +61,7 @@ set(LIBDATACHANNEL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerelement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerelement.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerrootelement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerrootelement.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpnackresponder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpnackresponder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
) )
@ -75,8 +75,7 @@ set(LIBDATACHANNEL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandler.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandler.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/init.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/global.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/log.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
@ -107,6 +106,7 @@ set(LIBDATACHANNEL_IMPL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/init.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.cpp
@ -129,6 +129,8 @@ set(LIBDATACHANNEL_IMPL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/init.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/internals.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/queue.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/queue.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.hpp
@ -213,17 +215,17 @@ set_target_properties(datachannel-static PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
CXX_STANDARD 17) CXX_STANDARD 17)
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(datachannel PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc) target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel PUBLIC Threads::Threads plog::plog) target_link_libraries(datachannel PUBLIC Threads::Threads)
target_link_libraries(datachannel PRIVATE Usrsctp::Usrsctp) target_link_libraries(datachannel PRIVATE Usrsctp::Usrsctp plog::plog)
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc) target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog) target_link_libraries(datachannel-static PUBLIC Threads::Threads)
target_link_libraries(datachannel-static PRIVATE Usrsctp::Usrsctp) target_link_libraries(datachannel-static PRIVATE Usrsctp::Usrsctp plog::plog)
if(WIN32) if(WIN32)
target_link_libraries(datachannel PUBLIC ws2_32) # winsock2 target_link_libraries(datachannel PUBLIC ws2_32) # winsock2
@ -310,9 +312,6 @@ endif()
add_library(LibDataChannel::LibDataChannel ALIAS datachannel) add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static) add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)
install(TARGETS datachannel LIBRARY DESTINATION lib)
install(FILES ${LIBDATACHANNEL_HEADERS} DESTINATION include/rtc)
if(NOT MSVC) if(NOT MSVC)
target_compile_options(datachannel PRIVATE -Wall -Wextra) target_compile_options(datachannel PRIVATE -Wall -Wextra)
target_compile_options(datachannel-static PRIVATE -Wall -Wextra) target_compile_options(datachannel-static PRIVATE -Wall -Wextra)
@ -328,6 +327,22 @@ if(WARNINGS_AS_ERRORS)
endif() endif()
endif() endif()
install(TARGETS datachannel EXPORT datachannel-export
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install(FILES ${LIBDATACHANNEL_HEADERS}
DESTINATION include/rtc
)
install(
EXPORT datachannel-export
NAMESPACE LibDatachannel::
DESTINATION share/cmake/libdatachannel
)
# Tests # Tests
if(NOT NO_TESTS) if(NOT NO_TESTS)
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")

6
DOC.md
View File

@ -88,7 +88,7 @@ Arguments:
- `iceServersCount` (optional): number of URLs in the array pointed by `iceServers` (0 if unused) - `iceServersCount` (optional): number of URLs in the array pointed by `iceServers` (0 if unused)
- `certificateType` (optional): certificate type, either `RTC_CERTIFICATE_ECDSA` or `RTC_CERTIFICATE_RSA` (0 or `RTC_CERTIFICATE_DEFAULT` if default) - `certificateType` (optional): certificate type, either `RTC_CERTIFICATE_ECDSA` or `RTC_CERTIFICATE_RSA` (0 or `RTC_CERTIFICATE_DEFAULT` if default)
- `enableIceTcp`: if true, generate TCP candidates for ICE (ignored with libjuice as ICE backend) - `enableIceTcp`: if true, generate TCP candidates for ICE (ignored with libjuice as ICE backend)
- `disableAutoNegociation`: if true, the user is responsible for calling `rtcSetLocalDescription` after creating a Data Channel and after setting the remote description - `disableAutoNegotiation`: if true, the user is responsible for calling `rtcSetLocalDescription` after creating a Data Channel and after setting the remote description
- `portRangeBegin` (optional): first port (included) of the allowed local port range (0 if unused) - `portRangeBegin` (optional): first port (included) of the allowed local port range (0 if unused)
- `portRangeEnd` (optional): last port (included) of the allowed local port (0 if unused) - `portRangeEnd` (optional): last port (included) of the allowed local port (0 if unused)
- `mtu` (optional): manually set the Maximum Transfer Unit (MTU) for the connection (0 if automatic) - `mtu` (optional): manually set the Maximum Transfer Unit (MTU) for the connection (0 if automatic)
@ -165,7 +165,7 @@ int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb)
int rtcSetLocalDescription(int pc, const char *type) int rtcSetLocalDescription(int pc, const char *type)
``` ```
Initiates the handshake process. Following this call, the local description callback will be called with the local description, which must be sent to the remote peer by the user's method of choice. Note this call is implicit after `rtcSetRemoteDescription` and `rtcCreateDataChannel` if `disableAutoNegociation` was not set on Peer Connection creation. Initiates the handshake process. Following this call, the local description callback will be called with the local description, which must be sent to the remote peer by the user's method of choice. Note this call is implicit after `rtcSetRemoteDescription` and `rtcCreateDataChannel` if `disableAutoNegotiation` was not set on Peer Connection creation.
Arguments: Arguments:
- `pc`: the Peer Connection identifier - `pc`: the Peer Connection identifier
@ -326,7 +326,7 @@ If `local`, `remote`, or both, are `NULL`, the corresponding candidate is not co
### Data Channel ### Data Channel
#### rtcAddDataChannel #### rtcCreateDataChannel
``` ```
int rtcCreateDataChannel(int pc, const char *label) int rtcCreateDataChannel(int pc, const char *label)

View File

@ -12,9 +12,12 @@ feature.compose <gnutls>on
lib libdatachannel lib libdatachannel
: # sources : # sources
[ glob ./src/*.cpp ] [ glob ./src/*.cpp ]
[ glob ./src/impl/*.cpp ]
: # requirements : # requirements
<cxxstd>17 <cxxstd>17
<include>./include
<include>./include/rtc <include>./include/rtc
<include>./src
<define>RTC_ENABLE_MEDIA=0 <define>RTC_ENABLE_MEDIA=0
<define>RTC_ENABLE_WEBSOCKET=0 <define>RTC_ENABLE_WEBSOCKET=0
<define>USE_NICE=0 <define>USE_NICE=0
@ -143,7 +146,7 @@ rule make_libjuice_openssl ( targets * : sources * : properties * )
{ {
# on macOS, default to pick up openssl from the homebrew installation # on macOS, default to pick up openssl from the homebrew installation
# brew install openssl # brew install openssl
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ; OPENSSL_INCLUDE = /opt/homebrew/opt/openssl /usr/local/opt/openssl/include ;
} }
if $(OPENSSL_INCLUDE) != "" if $(OPENSSL_INCLUDE) != ""
@ -191,7 +194,7 @@ rule openssl-lib-path ( properties * )
{ {
# on macOS, default to pick up openssl from the homebrew installation # on macOS, default to pick up openssl from the homebrew installation
# brew install openssl # brew install openssl
OPENSSL_LIB = /usr/local/opt/openssl/lib ; OPENSSL_LIB = /opt/homebrew/opt/openssl/lib /usr/local/opt/openssl/lib ;
} }
else if <target-os>windows in $(properties) && $(OPENSSL_LIB) = "" else if <target-os>windows in $(properties) && $(OPENSSL_LIB) = ""
{ {
@ -217,7 +220,7 @@ rule openssl-include-path ( properties * )
{ {
# on macOS, default to pick up openssl from the homebrew installation # on macOS, default to pick up openssl from the homebrew installation
# brew install openssl # brew install openssl
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ; OPENSSL_INCLUDE = /opt/homebrew/opt/openssl/include /usr/local/opt/openssl/include ;
} }
else if <target-os>windows in $(properties) && $(OPENSSL_INCLUDE) = "" else if <target-os>windows in $(properties) && $(OPENSSL_INCLUDE) = ""
{ {

View File

@ -2,7 +2,7 @@
libdatachannel is a standalone implementation of WebRTC Data Channels, WebRTC Media Transport, and WebSockets in C++17 with C bindings for POSIX platforms (including GNU/Linux, Android, and Apple macOS) and Microsoft Windows. libdatachannel is a standalone implementation of WebRTC Data Channels, WebRTC Media Transport, and WebSockets in C++17 with C bindings for POSIX platforms (including GNU/Linux, Android, and Apple macOS) and Microsoft Windows.
The library aims at being both straightforward and lightweight with a minimum of external dependencies, to enable direct connectivity between native applications and web browsers without the pain of importing Google's bloated [reference library](https://webrtc.googlesource.com/src/). The interface consists of somewhat simplified versions of the JavaScript WebRTC and WebSocket APIs present in browsers, in order to ease the design of cross-environment applications. The library aims at being both straightforward and lightweight with minimal external dependencies, to enable direct connectivity between native applications and web browsers without the pain of importing Google's bloated [reference library](https://webrtc.googlesource.com/src/). The interface consists of somewhat simplified versions of the JavaScript WebRTC and WebSocket APIs present in browsers, in order to ease the design of cross-environment applications.
It can be compiled with multiple backends: It can be compiled with multiple backends:
- The security layer can be provided through [OpenSSL](https://www.openssl.org/) or [GnuTLS](https://www.gnutls.org/). - The security layer can be provided through [OpenSSL](https://www.openssl.org/) or [GnuTLS](https://www.gnutls.org/).
@ -19,7 +19,7 @@ Only [GnuTLS](https://www.gnutls.org/) or [OpenSSL](https://www.openssl.org/) ar
Submodules: Submodules:
- libjuice: https://github.com/paullouisageneau/libjuice - libjuice: https://github.com/paullouisageneau/libjuice
- usrsctp: https://github.com/sctplab/usrsctp - usrsctp: https://github.com/sctplab/usrsctp
- libsrtp: https://github.com/cisco/libsrtp - libsrtp: https://github.com/cisco/libsrtp (if compiled with media support)
## Building ## Building
@ -124,6 +124,8 @@ The library implements the following communication protocols:
### WebRTC Data Channels and Media Transport ### WebRTC Data Channels and Media Transport
The library implements WebRTC Peer Connections with both Data Channels and Media Transport. Media transport is optional and can be disabled at compile time.
Protocol stack: Protocol stack:
- SCTP-based Data Channels ([RFC8831](https://tools.ietf.org/html/rfc8831)) - SCTP-based Data Channels ([RFC8831](https://tools.ietf.org/html/rfc8831))
- SRTP-based Media Transport ([RFC8834](https://tools.ietf.org/html/rfc8834)) - SRTP-based Media Transport ([RFC8834](https://tools.ietf.org/html/rfc8834))

2
deps/libjuice vendored

View File

@ -10,6 +10,7 @@ This directory contains different WebRTC clients and compatible WebSocket + JSON
- [signaling-server-rust](signaling-server-rust) contains a similar signaling server in Rust (see [lerouxrgd/datachannel-rs](https://github.com/lerouxrgd/datachannel-rs) for Rust wrappers) - [signaling-server-rust](signaling-server-rust) contains a similar signaling server in Rust (see [lerouxrgd/datachannel-rs](https://github.com/lerouxrgd/datachannel-rs) for Rust wrappers)
- [media](media) is a copy/paste demo to send the webcam from your browser into gstreamer. - [media](media) is a copy/paste demo to send the webcam from your browser into gstreamer.
- [sfu-media](sfu-media) is a copy/paste SFU demo to relay the webcam between browsers.
- [streamer](streamer) streams h264 and opus samples to web browsers (signaling-server-python is required). - [streamer](streamer) streams h264 and opus samples to web browsers (signaling-server-python is required).
Additionally, it contains two debugging tools for libdatachannel with copy-pasting as signaling: Additionally, it contains two debugging tools for libdatachannel with copy-pasting as signaling:

View File

@ -1,8 +1,9 @@
# libdatachannel - client-benchmark # libdatachannel - client-benchmark
This directory contains a native client to open Data Channels with WebSocket signaling using libdatachannel and benchmark functionalities. It offers two functionalities; This directory contains a native client to open Data Channels with WebSocket signaling using libdatachannel and benchmark functionalities. It offers three functionalities;
- Benchmark: Bi-directional data transfer benchmark (Also supports One-Way testing) - Benchmark: Bi-directional data transfer benchmark (Also supports One-Way testing)
- Constant Throughput Set: Send desired amount of data per second - Constant Throughput Set: Send desired amount of data per second
- Multiple Data Channel: Create desried count of data channel
## Start Signaling Server ## Start Signaling Server
- Start one of the signaling server from the examples folder. For example start `signaling-server-nodejs` like; - Start one of the signaling server from the examples folder. For example start `signaling-server-nodejs` like;
@ -12,7 +13,7 @@ This directory contains a native client to open Data Channels with WebSocket sig
## Start `client-benchmark` Applications ## Start `client-benchmark` Applications
Start 2 applications by using example calls below. Than copy one of the client's ID and paste to the other peer's screen to start offering process. Start 2 applications by using example calls below. Then copy one of the client's ID and paste to the other peer's screen to start offering process.
## Usage Examples ## Usage Examples
@ -23,63 +24,90 @@ Start 2 applications by using example calls below. Than copy one of the client's
Example Output (Offering Peer's Output); Example Output (Offering Peer's Output);
```bash ```bash
Stun server is stun:stun.l.google.com:19302 Stun server is stun:stun.l.google.com:19302
The local ID is: EQmF The local ID is: H1E3
Url is ws://localhost:8000/EQmF Url is ws://localhost:8000/H1E3
Waiting for signaling to be connected... Waiting for signaling to be connected...
2021-03-25 14:21:58.045 INFO [21386] [rtc::impl::TcpTransport::connect@159] Connected to localhost:8000 2021-04-10 19:51:31.319 INFO [16449] [rtc::impl::TcpTransport::connect@163] Connected to localhost:8000
2021-03-25 14:21:58.045 INFO [21386] [rtc::impl::TcpTransport::runLoop@327] TCP connected 2021-04-10 19:51:31.319 INFO [16449] [rtc::impl::TcpTransport::runLoop@331] TCP connected
2021-03-25 14:21:58.046 INFO [21386] [rtc::impl::WsTransport::incoming@118] WebSocket open 2021-04-10 19:51:31.321 INFO [16449] [rtc::impl::WsTransport::incoming@118] WebSocket open
WebSocket connected, signaling ready WebSocket connected, signaling ready
Enter a remote ID to send an offer: Enter a remote ID to send an offer:
0tDf n790
Offering to 0tDf Offering to n790
Creating DataChannel with label "benchmark" Creating DataChannel with label "DC-1"
2021-03-25 14:22:07.972 INFO [21379] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302" 2021-04-10 19:51:32.464 INFO [16442] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302"
2021-03-25 14:22:07.973 INFO [21379] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to new 2021-04-10 19:51:32.465 INFO [16442] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to new
2021-03-25 14:22:07.973 INFO [21379] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to in-progress 2021-04-10 19:51:32.465 INFO [16442] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to in-progress
Gathering State: in-progress Gathering State: in-progress
2021-03-25 14:22:07.974 INFO [21379] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering 2021-04-10 19:51:32.465 INFO [16442] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering
2021-03-25 14:22:07.974 WARN [21379] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:239: Local description already has the maximum number of host candidates
Benchmark will run for 300 seconds Benchmark will run for 300 seconds
2021-03-25 14:22:07.976 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting 2021-04-10 19:51:32.466 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting
2021-03-25 14:22:07.976 INFO [21396] [rtc::impl::PeerConnection::changeState@964] Changed state to connecting 2021-04-10 19:51:32.466 INFO [16450] [rtc::impl::PeerConnection::changeState@1016] Changed state to connecting
State: connecting State: connecting
2021-03-25 14:22:08.055 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302 2021-04-10 19:51:32.489 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302
2021-03-25 14:22:08.055 INFO [21386] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to connecting 2021-04-10 19:51:32.489 INFO [16449] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to connecting
2021-03-25 14:22:08.105 WARN [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:426: Send failed, errno=101 2021-04-10 19:51:32.490 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-03-25 14:22:08.105 WARN [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1483: STUN message send failed, errno=101 2021-04-10 19:51:32.491 INFO [16453] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-03-25 14:22:08.105 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN binding failed 2021-04-10 19:51:32.497 INFO [16443] [rtc::impl::SctpTransport::processNotification@713] SCTP connected
2021-03-25 14:22:08.107 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1302: STUN server binding successful 2021-04-10 19:51:32.497 INFO [16443] [rtc::impl::PeerConnection::changeState@1016] Changed state to connected
2021-03-25 14:22:08.107 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2148: Candidate gathering done
2021-03-25 14:22:08.107 INFO [21396] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to complete
Gathering State: complete
2021-03-25 14:22:08.155 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-03-25 14:22:08.206 INFO [21396] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
#1 Received: 0 KB/s Sent: 0 KB/s BufferSize: 0
2021-03-25 14:22:09.059 INFO [21399] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-03-25 14:22:09.069 INFO [21382] [rtc::impl::SctpTransport::processNotification@708] SCTP connected
2021-03-25 14:22:09.069 INFO [21382] [rtc::impl::PeerConnection::changeState@964] Changed state to connected
State: connected State: connected
DataChannel from 0tDf open DataChannel from n790 open
#2 Received: 41488 KB/s Sent: 42465 KB/s BufferSize: 65535 2021-04-10 19:51:32.542 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1328: STUN server binding successful
#3 Received: 43925 KB/s Sent: 43729 KB/s BufferSize: 65535 2021-04-10 19:51:32.589 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
#4 Received: 42491 KB/s Sent: 42361 KB/s BufferSize: 65535 #1
#5 Received: 45878 KB/s Sent: 45682 KB/s BufferSize: 65535 DC-1 Received: 40789 KB/s Sent: 41180 KB/s BufferSize: 65535
Stats# Received Total: 174 MB Sent Total: 175 MB RTT: 17 ms TOTL Received: 40789 KB/s Sent: 41180 KB/s
2021-04-10 19:51:34.039 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN server binding failed (timeout)
2021-04-10 19:51:34.039 INFO [16450] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2206: Candidate gathering done
2021-04-10 19:51:34.039 INFO [16450] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to complete
Gathering State: complete
#2
DC-1 Received: 41709 KB/s Sent: 41774 KB/s BufferSize: 65535
TOTL Received: 41709 KB/s Sent: 41774 KB/s
#3
DC-1 Received: 42165 KB/s Sent: 42360 KB/s BufferSize: 65535
TOTL Received: 42165 KB/s Sent: 42360 KB/s
#4
DC-1 Received: 42880 KB/s Sent: 42750 KB/s BufferSize: 65535
TOTL Received: 42880 KB/s Sent: 42750 KB/s
#5
DC-1 Received: 41771 KB/s Sent: 42097 KB/s BufferSize: 65535
TOTL Received: 41771 KB/s Sent: 42097 KB/s
Stats# Received Total: 210 MB Sent Total: 211 MB RTT: 20 ms
#6 Received: 43665 KB/s Sent: 43599 KB/s BufferSize: 65535 #6
#7 Received: 45749 KB/s Sent: 45488 KB/s BufferSize: 65535 DC-1 Received: 46235 KB/s Sent: 30433 KB/s BufferSize: 65535
#8 Received: 44055 KB/s Sent: 44055 KB/s BufferSize: 65535 TOTL Received: 46235 KB/s Sent: 30433 KB/s
#9 Received: 21572 KB/s Sent: 58199 KB/s BufferSize: 65535 #7
#10 Received: 22940 KB/s Sent: 55005 KB/s BufferSize: 65535 DC-1 Received: 47116 KB/s Sent: 28413 KB/s BufferSize: 65535
Stats# Received Total: 353 MB Sent Total: 422 MB RTT: 15 ms TOTL Received: 47116 KB/s Sent: 28413 KB/s
#8
DC-1 Received: 46923 KB/s Sent: 32520 KB/s BufferSize: 65535
TOTL Received: 46923 KB/s Sent: 32520 KB/s
#9
DC-1 Received: 44513 KB/s Sent: 34020 KB/s BufferSize: 65535
TOTL Received: 44513 KB/s Sent: 34020 KB/s
#10
DC-1 Received: 41966 KB/s Sent: 36166 KB/s BufferSize: 65535
TOTL Received: 41966 KB/s Sent: 36166 KB/s
Stats# Received Total: 438 MB Sent Total: 373 MB RTT: 19 ms
#11 Received: 27501 KB/s Sent: 53112 KB/s BufferSize: 65535 #11
#12 Received: 29914 KB/s Sent: 48162 KB/s BufferSize: 65535 DC-1 Received: 42617 KB/s Sent: 39619 KB/s BufferSize: 65535
#13 Received: 31869 KB/s Sent: 45946 KB/s BufferSize: 65535 TOTL Received: 42617 KB/s Sent: 39619 KB/s
#14 Received: 22484 KB/s Sent: 53636 KB/s BufferSize: 65535 #12
#15 Received: 16294 KB/s Sent: 56833 KB/s BufferSize: 65535 DC-1 Received: 43792 KB/s Sent: 43338 KB/s BufferSize: 65535
Stats# Received Total: 482 MB Sent Total: 682 MB RTT: 13 ms TOTL Received: 43792 KB/s Sent: 43338 KB/s
#13
DC-1 Received: 41715 KB/s Sent: 41585 KB/s BufferSize: 65535
TOTL Received: 41715 KB/s Sent: 41585 KB/s
#14
DC-1 Received: 39860 KB/s Sent: 33822 KB/s BufferSize: 65535
TOTL Received: 39860 KB/s Sent: 33822 KB/s
#15
DC-1 Received: 47576 KB/s Sent: 25352 KB/s BufferSize: 65535
TOTL Received: 47576 KB/s Sent: 25352 KB/s
Stats# Received Total: 655 MB Sent Total: 558 MB RTT: 13 ms
``` ```
### Benchmark for 300 seconds (Only Send, One Way) ### Benchmark for 300 seconds (Only Send, One Way)
@ -92,64 +120,92 @@ Start second peer as;
Example Output (Offering Peer's Output); Example Output (Offering Peer's Output);
```bash ```bash
Not Sending data. (One way benchmark).
Stun server is stun:stun.l.google.com:19302 Stun server is stun:stun.l.google.com:19302
The local ID is: XLLn The local ID is: 7EaP
Url is ws://localhost:8000/XLLn Url is ws://localhost:8000/7EaP
Waiting for signaling to be connected... Waiting for signaling to be connected...
2021-03-25 14:34:24.479 INFO [22332] [rtc::impl::TcpTransport::connect@159] Connected to localhost:8000 2021-04-10 19:54:36.857 INFO [16632] [rtc::impl::TcpTransport::connect@163] Connected to localhost:8000
2021-03-25 14:34:24.479 INFO [22332] [rtc::impl::TcpTransport::runLoop@327] TCP connected 2021-04-10 19:54:36.857 INFO [16632] [rtc::impl::TcpTransport::runLoop@331] TCP connected
2021-03-25 14:34:24.479 INFO [22332] [rtc::impl::WsTransport::incoming@118] WebSocket open 2021-04-10 19:54:36.858 INFO [16632] [rtc::impl::WsTransport::incoming@118] WebSocket open
WebSocket connected, signaling ready WebSocket connected, signaling ready
Enter a remote ID to send an offer: Enter a remote ID to send an offer:
Okt4 UDL4
Offering to Okt4 Offering to UDL4
Creating DataChannel with label "benchmark" Creating DataChannel with label "DC-1"
2021-03-25 14:34:37.948 INFO [22325] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302" 2021-04-10 19:54:53.381 INFO [16625] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302"
2021-03-25 14:34:37.949 INFO [22325] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to new 2021-04-10 19:54:53.382 INFO [16625] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to new
2021-03-25 14:34:37.949 INFO [22325] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to in-progress 2021-04-10 19:54:53.382 INFO [16625] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to in-progress
Gathering State: in-progress Gathering State: in-progress
2021-03-25 14:34:37.950 INFO [22325] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering 2021-04-10 19:54:53.383 INFO [16625] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering
2021-03-25 14:34:37.951 WARN [22325] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:239: Local description already has the maximum number of host candidates
Benchmark will run for 300 seconds Benchmark will run for 300 seconds
2021-03-25 14:34:37.952 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting 2021-04-10 19:54:53.384 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting
2021-03-25 14:34:37.952 INFO [22334] [rtc::impl::PeerConnection::changeState@964] Changed state to connecting 2021-04-10 19:54:53.384 INFO [16646] [rtc::impl::PeerConnection::changeState@1016] Changed state to connecting
State: connecting State: connecting
2021-03-25 14:34:37.969 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302 2021-04-10 19:54:53.475 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302
2021-03-25 14:34:37.969 INFO [22332] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to connecting 2021-04-10 19:54:53.475 INFO [16632] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to connecting
2021-03-25 14:34:37.970 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected 2021-04-10 19:54:53.527 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1328: STUN server binding successful
2021-03-25 14:34:37.971 INFO [22337] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished 2021-04-10 19:54:53.575 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-03-25 14:34:37.977 INFO [22327] [rtc::impl::SctpTransport::processNotification@708] SCTP connected 2021-04-10 19:54:53.625 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
2021-03-25 14:34:37.977 INFO [22327] [rtc::impl::PeerConnection::changeState@964] Changed state to connected #1
DC-1 Received: 0 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 0 KB/s Sent: 0 KB/s
2021-04-10 19:54:54.481 INFO [16653] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-04-10 19:54:54.491 INFO [16627] [rtc::impl::SctpTransport::processNotification@713] SCTP connected
2021-04-10 19:54:54.491 INFO [16627] [rtc::impl::PeerConnection::changeState@1016] Changed state to connected
State: connected State: connected
DataChannel from Okt4 open DataChannel from UDL4 open
2021-03-25 14:34:38.019 WARN [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:426: Send failed, errno=101 #2
2021-03-25 14:34:38.019 WARN [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1483: STUN message send failed, errno=101 DC-1 Received: 84326 KB/s Sent: 0 KB/s BufferSize: 0
2021-03-25 14:34:38.019 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN binding failed TOTL Received: 84326 KB/s Sent: 0 KB/s
2021-03-25 14:34:38.022 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1302: STUN server binding successful #3
2021-03-25 14:34:38.022 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2148: Candidate gathering done DC-1 Received: 99387 KB/s Sent: 0 KB/s BufferSize: 0
2021-03-25 14:34:38.022 INFO [22334] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to complete TOTL Received: 99387 KB/s Sent: 0 KB/s
2021-04-10 19:54:57.025 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN server binding failed (timeout)
2021-04-10 19:54:57.025 INFO [16646] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2206: Candidate gathering done
2021-04-10 19:54:57.025 INFO [16646] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to complete
Gathering State: complete Gathering State: complete
2021-03-25 14:34:38.069 INFO [22334] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed #4
#1 Received: 0 KB/s Sent: 92223 KB/s BufferSize: 65535 DC-1 Received: 94871 KB/s Sent: 0 KB/s BufferSize: 0
#2 Received: 0 KB/s Sent: 89291 KB/s BufferSize: 65535 TOTL Received: 94871 KB/s Sent: 0 KB/s
#3 Received: 0 KB/s Sent: 95087 KB/s BufferSize: 65535 #5
#4 Received: 0 KB/s Sent: 101050 KB/s BufferSize: 65535 DC-1 Received: 96259 KB/s Sent: 0 KB/s BufferSize: 0
#5 Received: 0 KB/s Sent: 99778 KB/s BufferSize: 0 TOTL Received: 96259 KB/s Sent: 0 KB/s
Stats# Received Total: 0 MB Sent Total: 480 MB RTT: 8 ms Stats# Received Total: 377 MB Sent Total: 0 MB RTT: 2 ms
#6 Received: 0 KB/s Sent: 100366 KB/s BufferSize: 65535 #6
#7 Received: 0 KB/s Sent: 101201 KB/s BufferSize: 65535 DC-1 Received: 92873 KB/s Sent: 0 KB/s BufferSize: 0
#8 Received: 0 KB/s Sent: 100892 KB/s BufferSize: 65535 TOTL Received: 92873 KB/s Sent: 0 KB/s
#9 Received: 0 KB/s Sent: 101288 KB/s BufferSize: 65535 #7
#10 Received: 0 KB/s Sent: 95676 KB/s BufferSize: 65535 DC-1 Received: 87724 KB/s Sent: 0 KB/s BufferSize: 0
Stats# Received Total: 0 MB Sent Total: 982 MB RTT: 8 ms TOTL Received: 87724 KB/s Sent: 0 KB/s
#8
DC-1 Received: 95123 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 95123 KB/s Sent: 0 KB/s
#9
DC-1 Received: 100022 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 100022 KB/s Sent: 0 KB/s
#10
DC-1 Received: 98124 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 98124 KB/s Sent: 0 KB/s
Stats# Received Total: 853 MB Sent Total: 0 MB RTT: 2 ms
#11 Received: 0 KB/s Sent: 96254 KB/s BufferSize: 65535 #11
#12 Received: 0 KB/s Sent: 105473 KB/s BufferSize: 65535 DC-1 Received: 103628 KB/s Sent: 0 KB/s BufferSize: 0
#13 Received: 0 KB/s Sent: 95549 KB/s BufferSize: 65535 TOTL Received: 103628 KB/s Sent: 0 KB/s
#14 Received: 0 KB/s Sent: 100366 KB/s BufferSize: 65535 #12
#15 Received: 0 KB/s Sent: 101336 KB/s BufferSize: 65535 DC-1 Received: 106166 KB/s Sent: 0 KB/s BufferSize: 0
Stats# Received Total: 0 MB Sent Total: 1484 MB RTT: 8 ms TOTL Received: 106166 KB/s Sent: 0 KB/s
#13
DC-1 Received: 98410 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 98410 KB/s Sent: 0 KB/s
#14
DC-1 Received: 99854 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 99854 KB/s Sent: 0 KB/s
#15
DC-1 Received: 98487 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 98487 KB/s Sent: 0 KB/s
Stats# Received Total: 1362 MB Sent Total: 0 MB RTT: 2 ms
``` ```
### Constant Throughput Set 8000 byte, for 300 seconds, send buffer 10000 byte ### Constant Throughput Set 8000 byte, for 300 seconds, send buffer 10000 byte
@ -159,61 +215,249 @@ Stats# Received Total: 0 MB Sent Total: 1484 MB RTT: 8 ms
Example Output (Offering Peer's Output); Example Output (Offering Peer's Output);
```bash ```bash
Stun server is stun:stun.l.google.com:19302 Stun server is stun:stun.l.google.com:19302
The local ID is: 1w9O The local ID is: 5zkC
Url is ws://localhost:8000/1w9O Url is ws://localhost:8000/5zkC
Waiting for signaling to be connected... Waiting for signaling to be connected...
2021-03-25 14:29:38.697 INFO [21844] [rtc::impl::TcpTransport::connect@159] Connected to localhost:8000 2021-04-10 19:52:49.788 INFO [16530] [rtc::impl::TcpTransport::connect@163] Connected to localhost:8000
2021-03-25 14:29:38.697 INFO [21844] [rtc::impl::TcpTransport::runLoop@327] TCP connected 2021-04-10 19:52:49.788 INFO [16530] [rtc::impl::TcpTransport::runLoop@331] TCP connected
2021-03-25 14:29:38.698 INFO [21844] [rtc::impl::WsTransport::incoming@118] WebSocket open 2021-04-10 19:52:49.789 INFO [16530] [rtc::impl::WsTransport::incoming@118] WebSocket open
WebSocket connected, signaling ready WebSocket connected, signaling ready
Enter a remote ID to send an offer: Enter a remote ID to send an offer:
zi4B WawD
Offering to zi4B Offering to WawD
Creating DataChannel with label "benchmark" Creating DataChannel with label "DC-1"
2021-03-25 14:29:48.729 INFO [21837] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302" 2021-04-10 19:52:57.720 INFO [16523] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302"
2021-03-25 14:29:48.729 INFO [21837] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to new 2021-04-10 19:52:57.721 INFO [16523] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to new
2021-03-25 14:29:48.729 INFO [21837] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to in-progress 2021-04-10 19:52:57.721 INFO [16523] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to in-progress
Gathering State: in-progress Gathering State: in-progress
2021-03-25 14:29:48.729 INFO [21837] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering 2021-04-10 19:52:57.722 INFO [16523] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering
2021-03-25 14:29:48.730 WARN [21837] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:239: Local description already has the maximum number of host candidates
Benchmark will run for 300 seconds Benchmark will run for 300 seconds
2021-03-25 14:29:48.730 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting 2021-04-10 19:52:57.722 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting
2021-03-25 14:29:48.731 INFO [21866] [rtc::impl::PeerConnection::changeState@964] Changed state to connecting 2021-04-10 19:52:57.722 INFO [16533] [rtc::impl::PeerConnection::changeState@1016] Changed state to connecting
State: connecting State: connecting
2021-03-25 14:29:48.732 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302 2021-04-10 19:52:57.725 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302
2021-03-25 14:29:48.732 INFO [21844] [rtc::impl::PeerConnection::changeSignalingState@992] Changed signaling state to connecting 2021-04-10 19:52:57.727 INFO [16530] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to connecting
2021-03-25 14:29:48.782 WARN [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:426: Send failed, errno=101 2021-04-10 19:52:57.826 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-03-25 14:29:48.782 WARN [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1483: STUN message send failed, errno=101 2021-04-10 19:52:57.828 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
2021-03-25 14:29:48.782 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN binding failed 2021-04-10 19:52:57.829 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1328: STUN server binding successful
2021-03-25 14:29:48.787 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1302: STUN server binding successful 2021-04-10 19:52:57.884 INFO [16535] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-03-25 14:29:48.787 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2148: Candidate gathering done 2021-04-10 19:52:57.907 INFO [16526] [rtc::impl::SctpTransport::processNotification@713] SCTP connected
2021-03-25 14:29:48.787 INFO [21866] [rtc::impl::PeerConnection::changeGatheringState@981] Changed gathering state to complete 2021-04-10 19:52:57.907 INFO [16526] [rtc::impl::PeerConnection::changeState@1016] Changed state to connected
Gathering State: complete
2021-03-25 14:29:48.832 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-03-25 14:29:48.882 INFO [21866] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
2021-03-25 14:29:49.735 INFO [21869] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-03-25 14:29:49.742 INFO [21841] [rtc::impl::SctpTransport::processNotification@708] SCTP connected
2021-03-25 14:29:49.742 INFO [21841] [rtc::impl::PeerConnection::changeState@964] Changed state to connected
State: connected State: connected
DataChannel from zi4B open DataChannel from WawD open
#1 Received: 0 KB/s Sent: 78 KB/s BufferSize: 0 #1
#2 Received: 8002 KB/s Sent: 7999 KB/s BufferSize: 0 DC-1 Received: 6515 KB/s Sent: 6577 KB/s BufferSize: 0
#3 Received: 8002 KB/s Sent: 7998 KB/s BufferSize: 0 TOTL Received: 6515 KB/s Sent: 6577 KB/s
#4 Received: 7995 KB/s Sent: 8000 KB/s BufferSize: 0 #2
#5 Received: 8000 KB/s Sent: 8001 KB/s BufferSize: 0 DC-1 Received: 7998 KB/s Sent: 7999 KB/s BufferSize: 0
Stats# Received Total: 33 MB Sent Total: 33 MB RTT: 0 ms TOTL Received: 7998 KB/s Sent: 7999 KB/s
#3
DC-1 Received: 7933 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 7933 KB/s Sent: 7999 KB/s
2021-04-10 19:53:01.275 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN server binding failed (timeout)
2021-04-10 19:53:01.275 INFO [16533] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2206: Candidate gathering done
2021-04-10 19:53:01.275 INFO [16533] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to complete
Gathering State: complete
#4
DC-1 Received: 8070 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 8070 KB/s Sent: 8000 KB/s
#5
DC-1 Received: 7984 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 7984 KB/s Sent: 8000 KB/s
Stats# Received Total: 39 MB Sent Total: 39 MB RTT: 0 ms
#6 Received: 8001 KB/s Sent: 7999 KB/s BufferSize: 0 #6
#7 Received: 7997 KB/s Sent: 7998 KB/s BufferSize: 0 DC-1 Received: 8004 KB/s Sent: 7998 KB/s BufferSize: 0
#8 Received: 8001 KB/s Sent: 7999 KB/s BufferSize: 0 TOTL Received: 8004 KB/s Sent: 7998 KB/s
#9 Received: 7998 KB/s Sent: 8001 KB/s BufferSize: 0 #7
#10 Received: 8003 KB/s Sent: 7998 KB/s BufferSize: 0 DC-1 Received: 7997 KB/s Sent: 8000 KB/s BufferSize: 0
Stats# Received Total: 74 MB Sent Total: 74 MB RTT: 0 ms TOTL Received: 7997 KB/s Sent: 8000 KB/s
#8
DC-1 Received: 8008 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 8008 KB/s Sent: 8000 KB/s
#9
DC-1 Received: 8007 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 8007 KB/s Sent: 8000 KB/s
#10
DC-1 Received: 7999 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 7999 KB/s Sent: 7999 KB/s
Stats# Received Total: 81 MB Sent Total: 81 MB RTT: 0 ms
#11 Received: 7990 KB/s Sent: 7998 KB/s BufferSize: 0 #11
#12 Received: 7999 KB/s Sent: 8000 KB/s BufferSize: 0 DC-1 Received: 7997 KB/s Sent: 8001 KB/s BufferSize: 0
#13 Received: 8001 KB/s Sent: 8002 KB/s BufferSize: 0 TOTL Received: 7997 KB/s Sent: 8001 KB/s
#14 Received: 7998 KB/s Sent: 7999 KB/s BufferSize: 0 #12
#15 Received: 8000 KB/s Sent: 7998 KB/s BufferSize: 0 DC-1 Received: 7981 KB/s Sent: 7997 KB/s BufferSize: 0
Stats# Received Total: 115 MB Sent Total: 115 MB RTT: 0 ms TOTL Received: 7981 KB/s Sent: 7997 KB/s
#13
DC-1 Received: 8024 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 8024 KB/s Sent: 8000 KB/s
#14
DC-1 Received: 7990 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 7990 KB/s Sent: 7999 KB/s
#15
DC-1 Received: 8001 KB/s Sent: 8002 KB/s BufferSize: 0
TOTL Received: 8001 KB/s Sent: 8002 KB/s
Stats# Received Total: 122 MB Sent Total: 122 MB RTT: 0 ms
```
### Constant Throughput Set 8000 byte, for 300 seconds, send buffer 10000 byte, 5 Data Channel
> `./client-benchmark -p -d 300 -r 8000 -b 10000 -c 5`
Example Output (Offering Peer's Output);
```bash
Stun server is stun:stun.l.google.com:19302
The local ID is: QZ46
Url is ws://localhost:8000/QZ46
Waiting for signaling to be connected...
2021-04-10 19:57:28.562 INFO [17117] [rtc::impl::TcpTransport::connect@163] Connected to localhost:8000
2021-04-10 19:57:28.562 INFO [17117] [rtc::impl::TcpTransport::runLoop@331] TCP connected
2021-04-10 19:57:28.563 INFO [17117] [rtc::impl::WsTransport::incoming@118] WebSocket open
WebSocket connected, signaling ready
Enter a remote ID to send an offer:
lTZA
Offering to lTZA
Creating DataChannel with label "DC-1"
2021-04-10 19:57:37.371 INFO [17110] [rtc::impl::IceTransport::IceTransport@106] Using STUN server "stun.l.google.com:19302"
2021-04-10 19:57:37.372 INFO [17110] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to new
2021-04-10 19:57:37.373 INFO [17110] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to in-progress
2021-04-10 19:57:37.373 INFO [17110] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to gathering
Gathering State: in-progress
Creating DataChannel with label "DC-2"
2021-04-10 19:57:37.373 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connecting
Creating DataChannel with label "DC-3"
2021-04-10 19:57:37.374 INFO [17119] [rtc::impl::PeerConnection::changeState@1016] Changed state to connecting
Creating DataChannel with label "DC-4"
Creating DataChannel with label "DC-5"
State: Benchmark will run for connecting300 seconds
2021-04-10 19:57:37.376 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:610: Using STUN server stun.l.google.com:19302
2021-04-10 19:57:37.378 INFO [17117] [rtc::impl::PeerConnection::changeSignalingState@1044] Changed signaling state to connecting
2021-04-10 19:57:37.423 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:1328: STUN server binding successful
2021-04-10 19:57:37.476 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to connected
2021-04-10 19:57:37.478 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:787: Changing state to completed
2021-04-10 19:57:38.383 INFO [17122] [rtc::impl::DtlsTransport::runRecvLoop@503] DTLS handshake finished
2021-04-10 19:57:38.392 INFO [17113] [rtc::impl::SctpTransport::processNotification@713] SCTP connected
2021-04-10 19:57:38.392 INFO [17113] [rtc::impl::PeerConnection::changeState@1016] Changed state to connected
State: connected
DataChannel from lTZA open
DataChannel from lTZA open
#DataChannel from lTZA open
DataChannel from lTZA open
1
DC-5 Received: 0 KB/s Sent: 79 KB/s BufferSize: 0
DC-3 Received: 0 KB/s Sent: 0 KB/s BufferSize: 0
DC-4 Received: 0 KB/s Sent: 79 KB/s BufferSize: 0
DC-2 Received: 0 KB/s Sent: 0 KB/s BufferSize: 0
DC-1 Received: 0 KB/s Sent: 0 KB/s BufferSize: 0
TOTL Received: 0 KB/s Sent: 158 KB/s
DataChannel from lTZA open
#2
DC-5 Received: 7960 KB/s Sent: 8000 KB/s BufferSize: 0
DC-3 Received: 7804 KB/s Sent: 8000 KB/s BufferSize: 0
DC-4 Received: 7883 KB/s Sent: 8000 KB/s BufferSize: 0
DC-2 Received: 7882 KB/s Sent: 8000 KB/s BufferSize: 0
DC-1 Received: 7804 KB/s Sent: 8000 KB/s BufferSize: 0
TOTL Received: 39333 KB/s Sent: 40000 KB/s
#3
DC-5 Received: 7966 KB/s Sent: 7996 KB/s BufferSize: 81504
DC-3 Received: 8047 KB/s Sent: 7996 KB/s BufferSize: 81504
DC-4 Received: 7958 KB/s Sent: 7996 KB/s BufferSize: 81504
DC-2 Received: 7958 KB/s Sent: 7996 KB/s BufferSize: 81504
DC-1 Received: 8067 KB/s Sent: 7996 KB/s BufferSize: 163597
TOTL Received: 39996 KB/s Sent: 39980 KB/s
2021-04-10 19:57:40.926 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:843: STUN server binding failed (timeout)
2021-04-10 19:57:40.926 INFO [17119] [rtc::impl::IceTransport::LogCallback@339] juice: agent.c:2206: Candidate gathering done
2021-04-10 19:57:40.926 INFO [17119] [rtc::impl::PeerConnection::changeGatheringState@1033] Changed gathering state to complete
Gathering State: complete
#4
DC-5 Received: 7970 KB/s Sent: 8002 KB/s BufferSize: 0
DC-3 Received: 7957 KB/s Sent: 8002 KB/s BufferSize: 0
DC-4 Received: 7910 KB/s Sent: 8002 KB/s BufferSize: 0
DC-2 Received: 7967 KB/s Sent: 8002 KB/s BufferSize: 0
DC-1 Received: 7957 KB/s Sent: 8002 KB/s BufferSize: 0
TOTL Received: 39761 KB/s Sent: 40010 KB/s
#5
DC-5 Received: 7996 KB/s Sent: 7999 KB/s BufferSize: 0
DC-3 Received: 8006 KB/s Sent: 7999 KB/s BufferSize: 0
DC-4 Received: 8078 KB/s Sent: 7999 KB/s BufferSize: 0
DC-2 Received: 8015 KB/s Sent: 7999 KB/s BufferSize: 0
DC-1 Received: 7928 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 40023 KB/s Sent: 39995 KB/s
Stats# Received Total: 165 MB Sent Total: 166 MB RTT: 1 ms
#6
DC-5 Received: 7968 KB/s Sent: 7999 KB/s BufferSize: 0
DC-3 Received: 7962 KB/s Sent: 7999 KB/s BufferSize: 0
DC-4 Received: 7965 KB/s Sent: 7999 KB/s BufferSize: 0
DC-2 Received: 7970 KB/s Sent: 7999 KB/s BufferSize: 0
DC-1 Received: 8044 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 39909 KB/s Sent: 39995 KB/s
#7
DC-5 Received: 6658 KB/s Sent: 8001 KB/s BufferSize: 82228
DC-3 Received: 6584 KB/s Sent: 8001 KB/s BufferSize: 163596
DC-4 Received: 6572 KB/s Sent: 8001 KB/s BufferSize: 163596
DC-2 Received: 6571 KB/s Sent: 8001 KB/s BufferSize: 163596
DC-1 Received: 6492 KB/s Sent: 8001 KB/s BufferSize: 163596
TOTL Received: 32877 KB/s Sent: 40005 KB/s
#8
DC-5 Received: 5773 KB/s Sent: 7997 KB/s BufferSize: 0
DC-3 Received: 6555 KB/s Sent: 7997 KB/s BufferSize: 0
DC-4 Received: 6164 KB/s Sent: 7997 KB/s BufferSize: 0
DC-2 Received: 6241 KB/s Sent: 7997 KB/s BufferSize: 0
DC-1 Received: 5454 KB/s Sent: 7997 KB/s BufferSize: 0
TOTL Received: 30187 KB/s Sent: 39985 KB/s
#9
DC-5 Received: 7442 KB/s Sent: 8002 KB/s BufferSize: 326921
DC-3 Received: 7580 KB/s Sent: 8002 KB/s BufferSize: 326921
DC-4 Received: 7363 KB/s Sent: 8002 KB/s BufferSize: 326921
DC-2 Received: 7524 KB/s Sent: 8002 KB/s BufferSize: 326921
DC-1 Received: 7362 KB/s Sent: 8002 KB/s BufferSize: 408769
TOTL Received: 37271 KB/s Sent: 40010 KB/s
#10
DC-5 Received: 6134 KB/s Sent: 7999 KB/s BufferSize: 244963
DC-3 Received: 8032 KB/s Sent: 7999 KB/s BufferSize: 326286
DC-4 Received: 5897 KB/s Sent: 7999 KB/s BufferSize: 326286
DC-2 Received: 5657 KB/s Sent: 7999 KB/s BufferSize: 326286
DC-1 Received: 5581 KB/s Sent: 7999 KB/s BufferSize: 326286
TOTL Received: 31301 KB/s Sent: 39995 KB/s
Stats# Received Total: 343 MB Sent Total: 372 MB RTT: 16 ms
#11
DC-5 Received: 6117 KB/s Sent: 7998 KB/s BufferSize: 570756
DC-3 Received: 6594 KB/s Sent: 7998 KB/s BufferSize: 570756
DC-4 Received: 6354 KB/s Sent: 7998 KB/s BufferSize: 570756
DC-2 Received: 6116 KB/s Sent: 7998 KB/s BufferSize: 570756
DC-1 Received: 5959 KB/s Sent: 7998 KB/s BufferSize: 570756
TOTL Received: 31140 KB/s Sent: 39990 KB/s
#12
DC-5 Received: 6840 KB/s Sent: 7999 KB/s BufferSize: 0
DC-3 Received: 7468 KB/s Sent: 7999 KB/s BufferSize: 0
DC-4 Received: 7472 KB/s Sent: 7999 KB/s BufferSize: 0
DC-2 Received: 7473 KB/s Sent: 7999 KB/s BufferSize: 0
DC-1 Received: 7236 KB/s Sent: 7999 KB/s BufferSize: 0
TOTL Received: 36489 KB/s Sent: 39995 KB/s
#13
DC-5 Received: 8105 KB/s Sent: 7989 KB/s BufferSize: 0
DC-3 Received: 8020 KB/s Sent: 7989 KB/s BufferSize: 0
DC-4 Received: 8097 KB/s Sent: 7989 KB/s BufferSize: 0
DC-2 Received: 8106 KB/s Sent: 7989 KB/s BufferSize: 0
DC-1 Received: 8018 KB/s Sent: 7989 KB/s BufferSize: 0
TOTL Received: 40346 KB/s Sent: 39945 KB/s
#14
DC-5 Received: 8042 KB/s Sent: 8007 KB/s BufferSize: 0
DC-3 Received: 8029 KB/s Sent: 8007 KB/s BufferSize: 0
DC-4 Received: 8038 KB/s Sent: 8007 KB/s BufferSize: 0
DC-2 Received: 8035 KB/s Sent: 8007 KB/s BufferSize: 0
DC-1 Received: 8036 KB/s Sent: 8007 KB/s BufferSize: 0
TOTL Received: 40180 KB/s Sent: 40035 KB/s
#15
DC-5 Received: 7981 KB/s Sent: 8001 KB/s BufferSize: 0
DC-3 Received: 7987 KB/s Sent: 8001 KB/s BufferSize: 0
DC-4 Received: 7980 KB/s Sent: 8001 KB/s BufferSize: 0
DC-2 Received: 7974 KB/s Sent: 8001 KB/s BufferSize: 0
DC-1 Received: 7972 KB/s Sent: 8001 KB/s BufferSize: 82497
TOTL Received: 39894 KB/s Sent: 40005 KB/s
Stats# Received Total: 538 MB Sent Total: 581 MB RTT: 3 ms
``` ```

View File

@ -31,6 +31,7 @@
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <future> #include <future>
#include <iomanip>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <random> #include <random>
@ -61,7 +62,8 @@ string randomId(size_t length);
// Benchmark // Benchmark
const size_t messageSize = 65535; const size_t messageSize = 65535;
binary messageData(messageSize); binary messageData(messageSize);
atomic<size_t> receivedSize = 0, sentSize = 0; unordered_map<string, atomic<size_t>> receivedSizeMap;
unordered_map<string, atomic<size_t>> sentSizeMap;
bool noSend = false; bool noSend = false;
// Benchmark - enableThroughputSet params // Benchmark - enableThroughputSet params
@ -69,7 +71,7 @@ bool enableThroughputSet;
int throughtputSetAsKB; int throughtputSetAsKB;
int bufferSize; int bufferSize;
const float STEP_COUNT_FOR_1_SEC = 100.0; const float STEP_COUNT_FOR_1_SEC = 100.0;
const int stepDurationInMs = 1000 / STEP_COUNT_FOR_1_SEC; const int stepDurationInMs = int(1000 / STEP_COUNT_FOR_1_SEC);
int main(int argc, char **argv) try { int main(int argc, char **argv) try {
Cmdline params(argc, argv); Cmdline params(argc, argv);
@ -188,14 +190,17 @@ int main(int argc, char **argv) try {
auto pc = createPeerConnection(config, ws, id); auto pc = createPeerConnection(config, ws, id);
// We are the offerer, so create a data channel to initiate the process // We are the offerer, so create a data channel to initiate the process
const string label = "benchmark"; for (int i = 1; i <= params.dataChannelCount(); i++) {
const string label = "DC-" + std::to_string(i);
cout << "Creating DataChannel with label \"" << label << "\"" << endl; cout << "Creating DataChannel with label \"" << label << "\"" << endl;
auto dc = pc->createDataChannel(label); auto dc = pc->createDataChannel(label);
receivedSizeMap.emplace(label, 0);
sentSizeMap.emplace(label, 0);
// Set Buffer Size // Set Buffer Size
dc->setBufferedAmountLowThreshold(bufferSize); dc->setBufferedAmountLowThreshold(bufferSize);
dc->onOpen([id, wdc = make_weak_ptr(dc)]() { dc->onOpen([id, wdc = make_weak_ptr(dc), label]() {
cout << "DataChannel from " << id << " open" << endl; cout << "DataChannel from " << id << " open" << endl;
if (noSend) if (noSend)
return; return;
@ -207,7 +212,7 @@ int main(int argc, char **argv) try {
try { try {
while (dcLocked->bufferedAmount() <= bufferSize) { while (dcLocked->bufferedAmount() <= bufferSize) {
dcLocked->send(messageData); dcLocked->send(messageData);
sentSize += messageData.size(); sentSizeMap.at(label) += messageData.size();
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
@ -215,7 +220,7 @@ int main(int argc, char **argv) try {
} }
}); });
dc->onBufferedAmountLow([wdc = make_weak_ptr(dc)]() { dc->onBufferedAmountLow([wdc = make_weak_ptr(dc), label]() {
if (noSend) if (noSend)
return; return;
@ -230,7 +235,7 @@ int main(int argc, char **argv) try {
try { try {
while (dcLocked->isOpen() && dcLocked->bufferedAmount() <= bufferSize) { while (dcLocked->isOpen() && dcLocked->bufferedAmount() <= bufferSize) {
dcLocked->send(messageData); dcLocked->send(messageData);
sentSize += messageData.size(); sentSizeMap.at(label) += messageData.size();
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
@ -239,12 +244,13 @@ int main(int argc, char **argv) try {
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; }); dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) { dc->onMessage([id, wdc = make_weak_ptr(dc), label](variant<binary, string> data) {
if (holds_alternative<binary>(data)) if (holds_alternative<binary>(data))
receivedSize += get<binary>(data).size(); receivedSizeMap.at(label) += get<binary>(data).size();
}); });
dataChannelMap.emplace(id, dc); dataChannelMap.emplace(label, dc);
}
const int duration = params.durationInSec() > 0 ? params.durationInSec() : INT32_MAX; const int duration = params.durationInSec() > 0 ? params.durationInSec() : INT32_MAX;
cout << "Benchmark will run for " << duration << " seconds" << endl; cout << "Benchmark will run for " << duration << " seconds" << endl;
@ -271,25 +277,39 @@ int main(int argc, char **argv) try {
binary tempMessageData(byteToSendThisLoop); binary tempMessageData(byteToSendThisLoop);
fill(tempMessageData.begin(), tempMessageData.end(), std::byte(0xFF)); fill(tempMessageData.begin(), tempMessageData.end(), std::byte(0xFF));
for (const auto &[label, dc] : dataChannelMap) {
if (dc->isOpen() && dc->bufferedAmount() <= bufferSize * byteToSendOnEveryLoop) { if (dc->isOpen() && dc->bufferedAmount() <= bufferSize * byteToSendOnEveryLoop) {
dc->send(tempMessageData); dc->send(tempMessageData);
sentSize += tempMessageData.size(); sentSizeMap.at(label) += tempMessageData.size();
}
} }
} }
if (printCounter >= STEP_COUNT_FOR_1_SEC) { if (printCounter >= STEP_COUNT_FOR_1_SEC) {
unsigned long _receivedSize = receivedSize.exchange(0);
unsigned long _sentSize = sentSize.exchange(0);
const double elapsedTimeInSecs = const double elapsedTimeInSecs =
std::chrono::duration<double>(steady_clock::now() - printTime).count(); std::chrono::duration<double>(steady_clock::now() - printTime).count();
printTime = steady_clock::now(); printTime = steady_clock::now();
cout << "#" << i / STEP_COUNT_FOR_1_SEC unsigned long receiveSpeedTotal = 0;
<< " Received: " << static_cast<int>(_receivedSize / (elapsedTimeInSecs * 1000)) unsigned long sendSpeedTotal = 0;
<< " KB/s" cout << "#" << i / STEP_COUNT_FOR_1_SEC << endl;
<< " Sent: " << static_cast<int>(_sentSize / (elapsedTimeInSecs * 1000)) for (const auto &[label, dc] : dataChannelMap) {
<< " KB/s" unsigned long channelReceiveSpeed = static_cast<int>(
receivedSizeMap[label].exchange(0) / (elapsedTimeInSecs * 1000));
unsigned long channelSendSpeed =
static_cast<int>(sentSizeMap[label].exchange(0) / (elapsedTimeInSecs * 1000));
cout << std::setw(10) << label << " Received: " << channelReceiveSpeed << " KB/s"
<< " Sent: " << channelSendSpeed << " KB/s"
<< " BufferSize: " << dc->bufferedAmount() << endl; << " BufferSize: " << dc->bufferedAmount() << endl;
receiveSpeedTotal += channelReceiveSpeed;
sendSpeedTotal += channelSendSpeed;
}
cout << std::setw(10) << "TOTL"
<< " Received: " << receiveSpeedTotal << " KB/s"
<< " Sent: " << sendSpeedTotal << " KB/s" << endl;
printStatCounter++; printStatCounter++;
printCounter = 0; printCounter = 0;
} }
@ -308,12 +328,16 @@ int main(int argc, char **argv) try {
dataChannelMap.clear(); dataChannelMap.clear();
peerConnectionMap.clear(); peerConnectionMap.clear();
receivedSizeMap.clear();
sentSizeMap.clear();
return 0; return 0;
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Error: " << e.what() << std::endl; std::cout << "Error: " << e.what() << std::endl;
dataChannelMap.clear(); dataChannelMap.clear();
peerConnectionMap.clear(); peerConnectionMap.clear();
receivedSizeMap.clear();
sentSizeMap.clear();
return -1; return -1;
} }
@ -346,13 +370,16 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
}); });
pc->onDataChannel([id](shared_ptr<DataChannel> dc) { pc->onDataChannel([id](shared_ptr<DataChannel> dc) {
cout << "DataChannel from " << id << " received with label \"" << dc->label() << "\"" const string label = dc->label();
<< endl; cout << "DataChannel from " << id << " received with label \"" << label << "\"" << endl;
cout << "###########################################" << endl; cout << "###########################################" << endl;
cout << "### Check other peer's screen for stats ###" << endl; cout << "### Check other peer's screen for stats ###" << endl;
cout << "###########################################" << endl; cout << "###########################################" << endl;
receivedSizeMap.emplace(dc->label(), 0);
sentSizeMap.emplace(dc->label(), 0);
// Set Buffer Size // Set Buffer Size
dc->setBufferedAmountLowThreshold(bufferSize); dc->setBufferedAmountLowThreshold(bufferSize);
@ -360,7 +387,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
try { try {
while (dc->bufferedAmount() <= bufferSize) { while (dc->bufferedAmount() <= bufferSize) {
dc->send(messageData); dc->send(messageData);
sentSize += messageData.size(); sentSizeMap.at(label) += messageData.size();
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
@ -370,7 +397,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
if (!noSend && enableThroughputSet) { if (!noSend && enableThroughputSet) {
// Create Send Data Thread // Create Send Data Thread
// Thread will join when data channel destroyed or closed // Thread will join when data channel destroyed or closed
std::thread([wdc = make_weak_ptr(dc)]() { std::thread([wdc = make_weak_ptr(dc), label]() {
steady_clock::time_point stepTime = steady_clock::now(); steady_clock::time_point stepTime = steady_clock::now();
// Byte count to send for every loop // Byte count to send for every loop
int byteToSendOnEveryLoop = throughtputSetAsKB * stepDurationInMs; int byteToSendOnEveryLoop = throughtputSetAsKB * stepDurationInMs;
@ -399,7 +426,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
if (dcLocked->bufferedAmount() <= bufferSize) { if (dcLocked->bufferedAmount() <= bufferSize) {
dcLocked->send(tempMessageData); dcLocked->send(tempMessageData);
sentSize += tempMessageData.size(); sentSizeMap.at(label) += tempMessageData.size();
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
@ -409,7 +436,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
}).detach(); }).detach();
} }
dc->onBufferedAmountLow([wdc = make_weak_ptr(dc)]() { dc->onBufferedAmountLow([wdc = make_weak_ptr(dc), label]() {
if (noSend) if (noSend)
return; return;
@ -424,7 +451,7 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
try { try {
while (dcLocked->isOpen() && dcLocked->bufferedAmount() <= bufferSize) { while (dcLocked->isOpen() && dcLocked->bufferedAmount() <= bufferSize) {
dcLocked->send(messageData); dcLocked->send(messageData);
sentSize += messageData.size(); sentSizeMap.at(label) += messageData.size();
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
@ -433,12 +460,12 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; }); dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) { dc->onMessage([id, wdc = make_weak_ptr(dc), label](variant<binary, string> data) {
if (holds_alternative<binary>(data)) if (holds_alternative<binary>(data))
receivedSize += get<binary>(data).size(); receivedSizeMap.at(label) += get<binary>(data).size();
}); });
dataChannelMap.emplace(id, dc); dataChannelMap.emplace(label, dc);
}); });
peerConnectionMap.emplace(id, pc); peerConnectionMap.emplace(id, pc);

View File

@ -51,6 +51,7 @@ Cmdline::Cmdline(int argc, char *argv[]) // ISO C++17 not allowed: throw (std::s
{"enableThroughputSet", no_argument, NULL, 'p'}, {"enableThroughputSet", no_argument, NULL, 'p'},
{"throughtputSetAsKB", required_argument, NULL, 'r'}, {"throughtputSetAsKB", required_argument, NULL, 'r'},
{"bufferSize", required_argument, NULL, 'b'}, {"bufferSize", required_argument, NULL, 'b'},
{"dataChannelCount", required_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}}; {NULL, 0, NULL, 0}};
@ -68,9 +69,10 @@ Cmdline::Cmdline(int argc, char *argv[]) // ISO C++17 not allowed: throw (std::s
_p = false; _p = false;
_r = 300; _r = 300;
_b = 0; _b = 0;
_c = 1;
optind = 0; optind = 0;
while ((c = getopt_long(argc, argv, "s:t:w:x:d:r:b:enhvop", long_options, &optind)) != -1) { while ((c = getopt_long(argc, argv, "s:t:w:x:d:r:b:c:enhvop", long_options, &optind)) != -1) {
switch (c) { switch (c) {
case 'n': case 'n':
_n = true; _n = true;
@ -147,6 +149,15 @@ Cmdline::Cmdline(int argc, char *argv[]) // ISO C++17 not allowed: throw (std::s
} }
break; break;
case 'c':
_c = atoi(optarg);
if (_c <= 0) {
std::string err;
err += "parameter range error: c must be > 0";
throw(std::range_error(err));
}
break;
case 'h': case 'h':
_h = true; _h = true;
this->usage(EXIT_SUCCESS); this->usage(EXIT_SUCCESS);
@ -196,6 +207,8 @@ libdatachannel client implementing WebRTC Data Channels with WebSocket signaling
Send a constant data per second (KB). See throughtputSetAsKB params.\n\ Send a constant data per second (KB). See throughtputSetAsKB params.\n\
[ -r ] [ --throughtputSetAsKB ] (type=INTEGER, range>0...INT_MAX, default=300)\n\ [ -r ] [ --throughtputSetAsKB ] (type=INTEGER, range>0...INT_MAX, default=300)\n\
Send constant data per second (KB).\n\ Send constant data per second (KB).\n\
[ -c ] [ --dataChannelCount ] (type=INTEGER, range>0...INT_MAX, default=1)\n\
Dat Channel count to create.\n\
[ -h ] [ --help ] (type=FLAG)\n\ [ -h ] [ --help ] (type=FLAG)\n\
Display this help and exit.\n"; Display this help and exit.\n";
} }

View File

@ -45,6 +45,7 @@ private:
bool _p; bool _p;
int _r; int _r;
int _b; int _b;
int _c;
/* other stuff to keep track of */ /* other stuff to keep track of */
std::string _program_name; std::string _program_name;
@ -72,6 +73,7 @@ public:
int bufferSize() const { return _b; } int bufferSize() const { return _b; }
bool enableThroughputSet () const { return _p; } bool enableThroughputSet () const { return _p; }
int throughtputSetAsKB() const { return _r; } int throughtputSetAsKB() const { return _r; }
int dataChannelCount() const { return _c; }
}; };
#endif #endif

View File

@ -222,9 +222,14 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
cout << "DataChannel from " << id << " received with label \"" << dc->label() << "\"" cout << "DataChannel from " << id << " received with label \"" << dc->label() << "\""
<< endl; << endl;
dc->onOpen([wdc = make_weak_ptr(dc)]() {
if (auto dc = wdc.lock())
dc->send("Hello from " + localId);
});
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; }); dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) { dc->onMessage([id](variant<binary, string> data) {
if (holds_alternative<string>(data)) if (holds_alternative<string>(data))
cout << "Message from " << id << " received: " << get<string>(data) << endl; cout << "Message from " << id << " received: " << get<string>(data) << endl;
else else
@ -232,8 +237,6 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
<< " received, size=" << get<binary>(data).size() << endl; << " received, size=" << get<binary>(data).size() << endl;
}); });
dc->send("Hello from " + localId);
dataChannelMap.emplace(id, dc); dataChannelMap.emplace(id, dc);
}); });
@ -251,4 +254,3 @@ string randomId(size_t length) {
generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); }); generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); });
return id; return id;
} }

View File

@ -9,7 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"license": "GPL-2.0", "license": "GPL-2.0",
"dependencies": { "dependencies": {
"websocket": "^1.0.33" "websocket": "^1.0.34"
} }
}, },
"node_modules/bufferutil": { "node_modules/bufferutil": {
@ -128,9 +128,9 @@
} }
}, },
"node_modules/websocket": { "node_modules/websocket": {
"version": "1.0.33", "version": "1.0.34",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
"integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==", "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
"dependencies": { "dependencies": {
"bufferutil": "^4.0.1", "bufferutil": "^4.0.1",
"debug": "^2.2.0", "debug": "^2.2.0",
@ -264,9 +264,9 @@
} }
}, },
"websocket": { "websocket": {
"version": "1.0.33", "version": "1.0.34",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
"integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==", "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
"requires": { "requires": {
"bufferutil": "^4.0.1", "bufferutil": "^4.0.1",
"debug": "^2.2.0", "debug": "^2.2.0",

View File

@ -2,7 +2,7 @@
"name": "libdatachannel-signaling-server", "name": "libdatachannel-signaling-server",
"version": "0.1.0", "version": "0.1.0",
"description": "Signaling server example for libdatachannel", "description": "Signaling server example for libdatachannel",
"main": "server.js", "main": "signaling-server.js",
"scripts": { "scripts": {
"start": "node signaling-server.js", "start": "node signaling-server.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@ -18,6 +18,6 @@
}, },
"homepage": "https://github.com/paullouisageneau/libdatachannel#readme", "homepage": "https://github.com/paullouisageneau/libdatachannel#readme",
"dependencies": { "dependencies": {
"websocket": "^1.0.33" "websocket": "^1.0.34"
} }
} }

View File

@ -17,7 +17,6 @@
* along with this program; If not, see <http://www.gnu.org/licenses/>. * along with this program; If not, see <http://www.gnu.org/licenses/>.
*/ */
const fs = require('fs');
const http = require('http'); const http = require('http');
const websocket = require('websocket'); const websocket = require('websocket');

View File

@ -17,9 +17,16 @@
*/ */
#include "h264fileparser.hpp" #include "h264fileparser.hpp"
#include <fstream>
#include "rtc/rtc.hpp" #include "rtc/rtc.hpp"
#include <fstream>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
using namespace std; using namespace std;
H264FileParser::H264FileParser(string directory, uint32_t fps, bool loop): FileParser(directory, ".h264", fps, loop) { } H264FileParser::H264FileParser(string directory, uint32_t fps, bool loop): FileParser(directory, ".h264", fps, loop) { }

View File

@ -19,10 +19,10 @@
#include "helpers.hpp" #include "helpers.hpp"
#include <ctime> #include <ctime>
#if _WIN32 #ifdef _WIN32
// taken from https://stackoverflow.com/questions/10905892/equivalent-of-gettimeday-for-windows // taken from https://stackoverflow.com/questions/10905892/equivalent-of-gettimeday-for-windows
#include <windows.h>
#include <Windows.h> #include <winsock2.h> // for struct timeval
struct timezone { struct timezone {
int tz_minuteswest; int tz_minuteswest;
@ -55,6 +55,8 @@ int gettimeofday(struct timeval *tv, struct timezone *tz)
} }
return 0; return 0;
} }
#else
#include <sys/time.h>
#endif #endif
using namespace std; using namespace std;

View File

@ -218,11 +218,11 @@ shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const
video.addSSRC(ssrc, cname, msid, cname); 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 = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate);
// create packetizer // create packetizer
auto packetizer = shared_ptr<H264RtpPacketizer>(new H264RtpPacketizer(H264RtpPacketizer::Separator::Length, rtpConfig)); auto packetizer = make_shared<H264RtpPacketizer>(H264RtpPacketizer::Separator::Length, rtpConfig);
// create H264 handler // create H264 handler
shared_ptr<H264PacketizationHandler> h264Handler(new H264PacketizationHandler(packetizer)); auto h264Handler = make_shared<H264PacketizationHandler>(packetizer);
// add RTCP SR handler // add RTCP SR handler
auto srReporter = make_shared<RtcpSrReporter>(rtpConfig); auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
h264Handler->addToChain(srReporter); h264Handler->addToChain(srReporter);
@ -242,7 +242,7 @@ shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const
audio.addSSRC(ssrc, cname, msid, cname); 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 = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate);
// create packetizer // create packetizer
auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig); auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig);
// create opus handler // create opus handler
@ -265,7 +265,7 @@ shared_ptr<Client> createPeerConnection(const Configuration &config,
weak_ptr<WebSocket> wws, weak_ptr<WebSocket> wws,
string id) { string id) {
auto pc = make_shared<PeerConnection>(config); auto pc = make_shared<PeerConnection>(config);
shared_ptr<Client> client(new Client(pc)); auto client = make_shared<Client>(pc);
pc->onStateChange([id](PeerConnection::State state) { pc->onStateChange([id](PeerConnection::State state) {
cout << "State: " << state << endl; cout << "State: " << state << endl;

View File

@ -19,7 +19,7 @@
#include "stream.hpp" #include "stream.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#if _WIN32 #ifdef _WIN32
// taken from https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw // taken from https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw
#include <windows.h> #include <windows.h>
@ -35,6 +35,8 @@ void usleep(__int64 usec)
WaitForSingleObject(timer, INFINITE); WaitForSingleObject(timer, INFINITE);
CloseHandle(timer); CloseHandle(timer);
} }
#else
#include <unistd.h>
#endif #endif
void StreamSource::stop() { void StreamSource::stop() {

View File

@ -118,7 +118,7 @@ function createPeerConnection(ws, id) {
pc.onconnectionstatechange = () => console.log(`Connection state: ${pc.connectionState}`); pc.onconnectionstatechange = () => console.log(`Connection state: ${pc.connectionState}`);
pc.onicegatheringstatechange = () => console.log(`Gathering state: ${pc.iceGatheringState}`); pc.onicegatheringstatechange = () => console.log(`Gathering state: ${pc.iceGatheringState}`);
pc.onicecandidate = (e) => { pc.onicecandidate = (e) => {
if (e.candidate) { if (e.candidate && e.candidate.candidate) {
// Send candidate // Send candidate
sendLocalCandidate(ws, id, e.candidate); sendLocalCandidate(ws, id, e.candidate);
} }

View File

@ -41,7 +41,6 @@
#include "rtc.h" // for C API defines #include "rtc.h" // for C API defines
#include "log.hpp"
#include "utils.hpp" #include "utils.hpp"
#include <cstddef> #include <cstddef>
@ -70,6 +69,7 @@ using binary = std::vector<byte>;
using binary_ptr = std::shared_ptr<binary>; using binary_ptr = std::shared_ptr<binary>;
using std::size_t; using std::size_t;
using std::ptrdiff_t;
using std::uint16_t; using std::uint16_t;
using std::uint32_t; using std::uint32_t;
using std::uint64_t; using std::uint64_t;

View File

@ -42,7 +42,14 @@ class RTC_CPP_EXPORT Description {
public: public:
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback }; enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
enum class Role { ActPass, Passive, Active }; enum class Role { ActPass, Passive, Active };
enum class Direction { SendOnly, RecvOnly, SendRecv, Inactive, Unknown };
enum class Direction {
SendOnly = RTC_DIRECTION_SENDONLY,
RecvOnly = RTC_DIRECTION_RECVONLY,
SendRecv = RTC_DIRECTION_SENDRECV,
Inactive = RTC_DIRECTION_INACTIVE,
Unknown = RTC_DIRECTION_UNKNOWN
};
Description(const string &sdp, Type type = Type::Unspec, Role role = Role::ActPass); Description(const string &sdp, Type type = Type::Unspec, Role role = Role::ActPass);
Description(const string &sdp, string typeString); Description(const string &sdp, string typeString);

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2019 Paul-Louis Ageneau * Copyright (c) 2020-2021 Paul-Louis Ageneau
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -16,27 +16,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_LOG_H #ifndef RTC_GLOBAL_H
#define RTC_LOG_H #define RTC_GLOBAL_H
// Disable warnings before including plog
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#elif defined(_MSC_VER)
#pragma warning(push, 0)
#endif
#include "plog/Log.h"
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "common.hpp" #include "common.hpp"
#include <chrono>
namespace rtc { namespace rtc {
enum class LogLevel { // Don't change, it must match plog severity enum class LogLevel { // Don't change, it must match plog severity
@ -49,8 +35,29 @@ enum class LogLevel { // Don't change, it must match plog severity
Verbose = 6 Verbose = 6
}; };
RTC_CPP_EXPORT void InitLogger(LogLevel level); typedef std::function<void(LogLevel level, string message)> LogCallback;
RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr);
#ifdef PLOG
RTC_CPP_EXPORT void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr); RTC_CPP_EXPORT void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
#endif
RTC_EXPORT void Preload();
RTC_EXPORT void Cleanup();
struct SctpSettings {
// For the following settings, not set means optimized default
optional<size_t> recvBufferSize; // in bytes
optional<size_t> sendBufferSize; // in bytes
optional<size_t> maxChunksOnQueue; // in chunks
optional<size_t> initialCongestionWindow; // in MTUs
optional<size_t> maxBurst; // in MTUs
optional<unsigned int> congestionControlModule; // 0: RFC2581, 1: HSTCP, 2: H-TCP, 3: RTCC
optional<std::chrono::milliseconds> delayedSackTime;
};
RTC_EXPORT void SetSctpSettings(SctpSettings s);
} // namespace rtc } // namespace rtc
#endif #endif

View File

@ -1,23 +1,23 @@
/* /**
* libdatachannel client example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef H264_PACKETIZATION_HANDLER_H #ifndef RTC_H264_PACKETIZATION_HANDLER_H
#define H264_PACKETIZATION_HANDLER_H #define RTC_H264_PACKETIZATION_HANDLER_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
@ -28,7 +28,7 @@
namespace rtc { namespace rtc {
/// Handler for H264 packetization /// Handler for H264 packetization
class RTC_CPP_EXPORT H264PacketizationHandler : public MediaChainableHandler { class RTC_CPP_EXPORT H264PacketizationHandler final : public MediaChainableHandler {
public: public:
/// Construct handler for H264 packetization. /// Construct handler for H264 packetization.
/// @param packetizer RTP packetizer for h264 /// @param packetizer RTP packetizer for h264
@ -39,4 +39,4 @@ public:
#endif /* RTC_ENABLE_MEDIA */ #endif /* RTC_ENABLE_MEDIA */
#endif /* H264_PACKETIZATION_HANDLER_H */ #endif /* RTC_H264_PACKETIZATION_HANDLER_H */

View File

@ -1,23 +1,23 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef H264_RTP_PACKETIZER_H #ifndef RTC_H264_RTP_PACKETIZER_H
#define H264_RTP_PACKETIZER_H #define RTC_H264_RTP_PACKETIZER_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
@ -28,13 +28,13 @@
namespace rtc { namespace rtc {
/// RTP packetization of h264 payload /// RTP packetization of h264 payload
class RTC_CPP_EXPORT H264RtpPacketizer : public RtpPacketizer, public MediaHandlerRootElement { class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer, public MediaHandlerRootElement {
shared_ptr<NalUnits> splitMessage(binary_ptr message); shared_ptr<NalUnits> splitMessage(binary_ptr message);
const uint16_t maximumFragmentSize; const uint16_t maximumFragmentSize;
public: public:
/// Default clock rate for H264 in RTP /// Default clock rate for H264 in RTP
static const auto defaultClockRate = 90 * 1000; inline static const uint32_t defaultClockRate = 90 * 1000;
/// Nalunit separator /// Nalunit separator
enum class Separator { enum class Separator {
@ -64,4 +64,4 @@ private:
#endif /* RTC_ENABLE_MEDIA */ #endif /* RTC_ENABLE_MEDIA */
#endif /* H264_RTP_PACKETIZER_H */ #endif /* RTC_H264_RTP_PACKETIZER_H */

View File

@ -1,18 +1,19 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_MEDIA_CHAINABLE_HANDLER_H #ifndef RTC_MEDIA_CHAINABLE_HANDLER_H

View File

@ -1,18 +1,19 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_MEDIA_HANDLER_ELEMENT_H #ifndef RTC_MEDIA_HANDLER_ELEMENT_H

View File

@ -1,22 +1,23 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H #ifndef RTC_MEDIA_HANDLER_ROOT_ELEMENT_H
#define RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H #define RTC_MEDIA_HANDLER_ROOT_ELEMENT_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
@ -42,4 +43,4 @@ public:
#endif // RTC_ENABLE_MEDIA #endif // RTC_ENABLE_MEDIA
#endif // RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H #endif // RTC_MEDIA_HANDLER_ROOT_ELEMENT_H

View File

@ -1,34 +1,38 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef NAL_UNIT_H #ifndef RTC_NAL_UNIT_H
#define NAL_UNIT_H #define RTC_NAL_UNIT_H
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "common.hpp" #include "common.hpp"
#include <cassert>
namespace rtc { namespace rtc {
#pragma pack(push, 1) #pragma pack(push, 1)
/// Nalu header /// Nalu header
struct RTC_CPP_EXPORT NalUnitHeader { struct RTC_CPP_EXPORT NalUnitHeader {
uint8_t _first = 0;
bool forbiddenBit() { return _first >> 7; } bool forbiddenBit() { return _first >> 7; }
uint8_t nri() { return _first >> 5 & 0x03; } uint8_t nri() { return _first >> 5 & 0x03; }
uint8_t unitType() { return _first & 0x1F; } uint8_t unitType() { return _first & 0x1F; }
@ -36,13 +40,12 @@ struct RTC_CPP_EXPORT NalUnitHeader {
void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); } void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
void setNRI(uint8_t nri) { _first = (_first & 0x9F) | ((nri & 0x03) << 5); } void setNRI(uint8_t nri) { _first = (_first & 0x9F) | ((nri & 0x03) << 5); }
void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); } void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); }
private:
uint8_t _first = 0;
}; };
/// Nalu fragment header /// Nalu fragment header
struct RTC_CPP_EXPORT NalUnitFragmentHeader { struct RTC_CPP_EXPORT NalUnitFragmentHeader {
uint8_t _first = 0;
bool isStart() { return _first >> 7; } bool isStart() { return _first >> 7; }
bool reservedBit6() { return (_first >> 6) & 0x01; } bool reservedBit6() { return (_first >> 6) & 0x01; }
bool isEnd() { return (_first >> 5) & 0x01; } bool isEnd() { return (_first >> 5) & 0x01; }
@ -52,9 +55,6 @@ struct RTC_CPP_EXPORT NalUnitFragmentHeader {
void setEnd(bool isSet) { _first = (_first & 0xDF) | (isSet << 6); } void setEnd(bool isSet) { _first = (_first & 0xDF) | (isSet << 6); }
void setReservedBit6(bool isSet) { _first = (_first & 0xBF) | (isSet << 5); } void setReservedBit6(bool isSet) { _first = (_first & 0xBF) | (isSet << 5); }
void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); } void setUnitType(uint8_t type) { _first = (_first & 0xE0) | (type & 0x1F); }
private:
uint8_t _first = 0;
}; };
#pragma pack(pop) #pragma pack(pop)
@ -152,4 +152,4 @@ public:
#endif /* RTC_ENABLE_MEDIA */ #endif /* RTC_ENABLE_MEDIA */
#endif /* NAL_UNIT_H */ #endif /* RTC_NAL_UNIT_H */

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel client example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_OPUS_PACKETIZATION_HANDLER_H #ifndef RTC_OPUS_PACKETIZATION_HANDLER_H
@ -27,7 +27,7 @@
namespace rtc { namespace rtc {
/// Handler for opus packetization /// Handler for opus packetization
class RTC_CPP_EXPORT OpusPacketizationHandler : public MediaChainableHandler { class RTC_CPP_EXPORT OpusPacketizationHandler final : public MediaChainableHandler {
public: public:
/// Construct handler for opus packetization. /// Construct handler for opus packetization.

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_OPUS_RTP_PACKETIZER_H #ifndef RTC_OPUS_RTP_PACKETIZER_H
@ -27,10 +27,10 @@
namespace rtc { namespace rtc {
/// RTP packetizer for opus /// RTP packetizer for opus
class RTC_CPP_EXPORT OpusRtpPacketizer : public RtpPacketizer, public MediaHandlerRootElement { class RTC_CPP_EXPORT OpusRtpPacketizer final : public RtpPacketizer, public MediaHandlerRootElement {
public: public:
/// default clock rate used in opus RTP communication /// default clock rate used in opus RTP communication
static const uint32_t defaultClockRate = 48 * 1000; inline static const uint32_t defaultClockRate = 48 * 1000;
/// Constructs opus packetizer with given RTP configuration. /// Constructs opus packetizer with given RTP configuration.
/// @note RTP configuration is used in packetization process which may change some configuration /// @note RTP configuration is used in packetization process which may change some configuration

View File

@ -24,7 +24,6 @@
#include "datachannel.hpp" #include "datachannel.hpp"
#include "description.hpp" #include "description.hpp"
#include "common.hpp" #include "common.hpp"
#include "init.hpp"
#include "message.hpp" #include "message.hpp"
#include "reliability.hpp" #include "reliability.hpp"
#include "track.hpp" #include "track.hpp"
@ -82,8 +81,6 @@ public:
State state() const; State state() const;
GatheringState gatheringState() const; GatheringState gatheringState() const;
SignalingState signalingState() const; SignalingState signalingState() const;
bool hasLocalDescription() const;
bool hasRemoteDescription() const;
bool hasMedia() const; bool hasMedia() const;
optional<Description> localDescription() const; optional<Description> localDescription() const;
optional<Description> remoteDescription() const; optional<Description> remoteDescription() const;

View File

@ -46,7 +46,8 @@ extern "C" {
#define RTC_DEFAULT_MTU 1280 // IPv6 minimum guaranteed MTU #define RTC_DEFAULT_MTU 1280 // IPv6 minimum guaranteed MTU
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#define RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE ((uint16_t)(RTC_DEFAULT_MTU - 12 - 8 - 40)) // SRTP/UDP/IPv6 #define RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE \
((uint16_t)(RTC_DEFAULT_MTU - 12 - 8 - 40)) // SRTP/UDP/IPv6
#define RTC_DEFAULT_MAXIMUM_PACKET_COUNT_FOR_NACK_CACHE ((unsigned)512) #define RTC_DEFAULT_MAXIMUM_PACKET_COUNT_FOR_NACK_CACHE ((unsigned)512)
#endif #endif
@ -94,8 +95,6 @@ typedef enum {
RTC_CERTIFICATE_RSA = 2, RTC_CERTIFICATE_RSA = 2,
} rtcCertificateType; } rtcCertificateType;
#if RTC_ENABLE_MEDIA
typedef enum { typedef enum {
// video // video
RTC_CODEC_H264 = 0, RTC_CODEC_H264 = 0,
@ -114,41 +113,12 @@ typedef enum {
RTC_DIRECTION_INACTIVE = 4 RTC_DIRECTION_INACTIVE = 4
} rtcDirection; } rtcDirection;
#endif // RTC_ENABLE_MEDIA
#define RTC_ERR_SUCCESS 0 #define RTC_ERR_SUCCESS 0
#define RTC_ERR_INVALID -1 // invalid argument #define RTC_ERR_INVALID -1 // invalid argument
#define RTC_ERR_FAILURE -2 // runtime error #define RTC_ERR_FAILURE -2 // runtime error
#define RTC_ERR_NOT_AVAIL -3 // element not available #define RTC_ERR_NOT_AVAIL -3 // element not available
#define RTC_ERR_TOO_SMALL -4 // buffer too small #define RTC_ERR_TOO_SMALL -4 // buffer too small
typedef struct {
const char **iceServers;
int iceServersCount;
rtcCertificateType certificateType;
bool enableIceTcp;
bool disableAutoNegotiation;
uint16_t portRangeBegin;
uint16_t portRangeEnd;
int mtu; // <= 0 means automatic
int maxMessageSize; // <= 0 means default
} rtcConfiguration;
typedef struct {
bool unordered;
bool unreliable;
int maxPacketLifeTime; // ignored if reliable
int maxRetransmits; // ignored if reliable
} rtcReliability;
typedef struct {
rtcReliability reliability;
const char *protocol; // empty string if NULL
bool negotiated;
bool manualStream;
uint16_t stream; // numeric ID 0-65534, ignored if manualStream is false
} rtcDataChannelInit;
typedef void(RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message); typedef void(RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const char *type, typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const char *type,
void *ptr); void *ptr);
@ -167,6 +137,7 @@ typedef void(RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr);
typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr); typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr);
// Log // Log
// NULL cb on the first call will log to stdout // NULL cb on the first call will log to stdout
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb);
@ -175,6 +146,19 @@ RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
RTC_EXPORT void *rtcGetUserPointer(int i); RTC_EXPORT void *rtcGetUserPointer(int i);
// PeerConnection // PeerConnection
typedef struct {
const char **iceServers;
int iceServersCount;
rtcCertificateType certificateType;
bool enableIceTcp;
bool disableAutoNegotiation;
uint16_t portRangeBegin;
uint16_t portRangeEnd;
int mtu; // <= 0 means automatic
int maxMessageSize; // <= 0 means default
} rtcConfiguration;
RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
RTC_EXPORT int rtcDeletePeerConnection(int pc); RTC_EXPORT int rtcDeletePeerConnection(int pc);
@ -201,6 +185,22 @@ RTC_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, c
int remoteSize); int remoteSize);
// DataChannel // DataChannel
typedef struct {
bool unordered;
bool unreliable;
int maxPacketLifeTime; // ignored if reliable
int maxRetransmits; // ignored if reliable
} rtcReliability;
typedef struct {
rtcReliability reliability;
const char *protocol; // empty string if NULL
bool negotiated;
bool manualStream;
uint16_t stream; // numeric ID 0-65534, ignored if manualStream is false
} rtcDataChannelInit;
RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb); RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
RTC_EXPORT int rtcCreateDataChannelEx(int pc, const char *label, RTC_EXPORT int rtcCreateDataChannelEx(int pc, const char *label,
@ -213,108 +213,96 @@ RTC_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size);
RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability); RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability);
// Track // Track
typedef struct {
rtcDirection direction;
rtcCodec codec;
int payloadType;
uint32_t ssrc;
const char *mid;
const char *name; // optional
const char *msid; // optional
const char *trackId; // optional, track ID used in MSID
} rtcTrackInit;
RTC_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb); RTC_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb);
RTC_EXPORT int rtcAddTrack(int pc, const char *mediaDescriptionSdp); // returns tr id RTC_EXPORT int rtcAddTrack(int pc, const char *mediaDescriptionSdp); // returns tr id
RTC_EXPORT int rtcAddTrackEx(int pc, const rtcTrackInit *init); // returns tr id
RTC_EXPORT int rtcDeleteTrack(int tr); RTC_EXPORT int rtcDeleteTrack(int tr);
RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size); RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
// Media
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
/// Add track // Media
/// @param pc Peer connection id
/// @param codec Codec
/// @param payloadType Payload type
/// @param ssrc SSRC
/// @param _mid MID
/// @param _direction Direction
/// @param _name Name (optional)
/// @param _msid MSID (optional)
/// @param _trackID Track ID used in MSID (optional)
/// @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, const char *_trackID);
/// Set H264PacketizationHandler for track typedef struct {
/// @param tr Track id uint32_t ssrc;
/// @param ssrc SSRC const char *cname;
/// @param cname CName uint8_t payloadType;
/// @param payloadType Payload Type uint32_t clockRate;
/// @param clockRate Clock rate uint16_t maxFragmentSize; // Maximum NALU fragment size
/// @param maxFragmentSize Maximum NALU fragment size uint16_t sequenceNumber;
/// @param sequenceNumber Sequence number uint32_t timestamp;
/// @param timestamp Timestamp } rtcPacketizationHandlerInit;
RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t maxFragmentSize, uint16_t sequenceNumber, uint32_t timestamp);
/// Set OpusPacketizationHandler for track typedef struct {
/// @param tr Track id double seconds; // Start time in seconds
/// @param ssrc SSRC bool since1970; // true if seconds since 1970
/// @param cname CName // false if seconds since 1900
/// @param payloadType Payload Type uint32_t timestamp; // Start timestamp
/// @param clockRate Clock rate } rtcStartTime;
/// @param _sequenceNumber Sequence number
/// @param _timestamp Timestamp
RTC_EXPORT int rtcSetOpusPacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t _sequenceNumber, uint32_t _timestamp);
/// Chain RtcpSrReporter to handler chain for given track // Set H264PacketizationHandler for track
/// @param tr Track id RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init);
int rtcChainRtcpSrReporter(int tr);
/// Chain RtcpNackResponder to handler chain for given track // Set OpusPacketizationHandler for track
/// @param tr Track id RTC_EXPORT int rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init);
/// @param maxStoredPacketsCount Maximum stored packet count
int rtcChainRtcpNackResponder(int tr, unsigned maxStoredPacketsCount); // Chain RtcpSrReporter to handler chain for given track
RTC_EXPORT int rtcChainRtcpSrReporter(int tr);
// Chain RtcpNackResponder to handler chain for given track
RTC_EXPORT int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount);
/// Set start time for RTP stream /// Set start time for RTP stream
/// @param startTime_s Start time in seconds RTC_EXPORT int rtcSetRtpConfigurationStartTime(int id, const rtcStartTime *startTime);
/// @param timeIntervalSince1970 Set true if `startTime_s` is time interval since 1970, false if `startTime_s` is time interval since 1900
/// @param _timestamp Start timestamp
int rtcSetRtpConfigurationStartTime(int id, double startTime_s, bool timeIntervalSince1970, uint32_t _timestamp);
/// Start stats recording for RTCP Sender Reporter // Start stats recording for RTCP Sender Reporter
/// @param id Track identifier RTC_EXPORT int rtcStartRtcpSenderReporterRecording(int id);
int rtcStartRtcpSenderReporterRecording(int id);
/// Transform seconds to timestamp using track's clock rate // Transform seconds to timestamp using track's clock rate
/// @param id Track id // Result is written to timestamp
/// @param seconds Seconds RTC_EXPORT int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp);
/// @param timestamp Pointer to result
int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t * timestamp);
/// Transform timestamp to seconds using track's clock rate // Transform timestamp to seconds using track's clock rate
/// @param id Track id // Result is written to seconds
/// @param timestamp Timestamp RTC_EXPORT int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds);
/// @param seconds Pointer for result
int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double * seconds);
/// Get current timestamp // Get current timestamp
/// @param id Track id // Result is written to timestamp
/// @param timestamp Pointer for result RTC_EXPORT int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp);
int rtcGetCurrentTrackTimestamp(int id, uint32_t * timestamp);
/// Get start timestamp for track identified by given id // Get start timestamp for track identified by given id
/// @param id Track id // Result is written to timestamp
/// @param timestamp Pointer for result RTC_EXPORT int rtcGetTrackStartTimestamp(int id, uint32_t *timestamp);
int rtcGetTrackStartTimestamp(int id, uint32_t * timestamp);
/// Set RTP timestamp for track identified by given id // Set RTP timestamp for track identified by given id
/// @param id Track id RTC_EXPORT int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp);
/// @param timestamp New timestamp
int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp);
/// Get timestamp of previous RTCP SR // Get timestamp of previous RTCP SR
/// @param id Track id // Result is written to timestamp
/// @param timestamp Pointer for result RTC_EXPORT int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timestamp);
int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t * timestamp);
/// Set `NeedsToReport` flag in RtcpSrReporter handler identified by given track id // Set NeedsToReport flag in RtcpSrReporter handler identified by given track id
/// @param id Track id RTC_EXPORT int rtcSetNeedsToSendRtcpSr(int id);
int rtcSetNeedsToSendRtcpSr(int id);
#endif // RTC_ENABLE_MEDIA #endif // RTC_ENABLE_MEDIA
// WebSocket
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
// WebSocket
typedef struct { typedef struct {
bool disableTlsVerification; // if true, don't verify the TLS certificate bool disableTlsVerification; // if true, don't verify the TLS certificate
} rtcWsConfiguration; } rtcWsConfiguration;
@ -322,9 +310,11 @@ typedef struct {
RTC_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id RTC_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id
RTC_EXPORT int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config); RTC_EXPORT int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config);
RTC_EXPORT int rtcDeleteWebsocket(int ws); RTC_EXPORT int rtcDeleteWebsocket(int ws);
#endif #endif
// DataChannel, Track, and WebSocket common API // DataChannel, Track, and WebSocket common API
RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb); RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb);
RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb); RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb);
RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb); RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb);
@ -336,14 +326,31 @@ RTC_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb); RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
// DataChannel, Track, and WebSocket common extended API // DataChannel, Track, and WebSocket common extended API
RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive
RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb); RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb);
RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size); RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
// Optional preload and cleanup // Optional global preload and cleanup
RTC_EXPORT void rtcPreload(void); RTC_EXPORT void rtcPreload(void);
RTC_EXPORT void rtcCleanup(void); RTC_EXPORT void rtcCleanup(void);
// SCTP global settings
typedef struct {
int recvBufferSize; // in bytes, <= 0 means optimized default
int sendBufferSize; // in bytes, <= 0 means optimized default
int maxChunksOnQueue; // in chunks, <= 0 means optimized default
int initialCongestionWindow; // in MTUs, <= 0 means optimized default
int maxBurst; // in MTUs, 0 means optimized default, < 0 means disabled
int congestionControlModule; // 0: RFC2581 (default), 1: HSTCP, 2: H-TCP, 3: RTCC
int delayedSackTimeMs; // in msecs, <= 0 means optimized default
} rtcSctpSettings;
// Note: SCTP settings apply to newly-created PeerConnections only
RTC_EXPORT int rtcSetSctpSettings(const rtcSctpSettings *settings);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View File

@ -21,12 +21,11 @@
// C++ API // C++ API
#include "common.hpp" #include "common.hpp"
#include "init.hpp" // for rtc::Cleanup() #include "global.hpp"
#include "log.hpp"
// //
#include "datachannel.hpp" #include "datachannel.hpp"
#include "track.hpp"
#include "peerconnection.hpp" #include "peerconnection.hpp"
#include "track.hpp"
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
@ -38,14 +37,13 @@
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
// Media handling // Media handling
#include "rtcpreceivingsession.hpp"
#include "mediachainablehandler.hpp" #include "mediachainablehandler.hpp"
#include "rtcpsrreporter.hpp"
#include "rtcpnackresponder.hpp" #include "rtcpnackresponder.hpp"
#include "rtcpreceivingsession.hpp"
#include "rtcpsrreporter.hpp"
// Opus/h264 streaming // Opus/h264 streaming
#include "h264packetizationhandler.hpp" #include "h264packetizationhandler.hpp"
#include "opuspacketizationhandler.hpp" #include "opuspacketizationhandler.hpp"
#endif // RTC_ENABLE_MEDIA #endif // RTC_ENABLE_MEDIA

View File

@ -1,18 +1,19 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_RTCP_NACK_RESPONDER_H #ifndef RTC_RTCP_NACK_RESPONDER_H
@ -27,7 +28,7 @@
namespace rtc { namespace rtc {
class RTC_CPP_EXPORT RtcpNackResponder: public MediaHandlerElement { class RTC_CPP_EXPORT RtcpNackResponder final: public MediaHandlerElement {
/// Packet storage /// Packet storage
class RTC_CPP_EXPORT Storage { class RTC_CPP_EXPORT Storage {

View File

@ -1,18 +1,19 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_RTCP_SENDER_REPORTABLE_H #ifndef RTC_RTCP_SENDER_REPORTABLE_H
@ -26,7 +27,7 @@
namespace rtc { namespace rtc {
class RTC_CPP_EXPORT RtcpSrReporter: public MediaHandlerElement { class RTC_CPP_EXPORT RtcpSrReporter final: public MediaHandlerElement {
bool needsToReport = false; bool needsToReport = false;

View File

@ -21,23 +21,9 @@
#ifndef RTC_RTP_HPP #ifndef RTC_RTP_HPP
#define RTC_RTP_HPP #define RTC_RTP_HPP
#include "log.hpp" #include "common.hpp"
#include <cmath> #include <vector>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#ifndef htonll
#define htonll(x) \
((uint64_t)htonl(((uint64_t)(x)&0xFFFFFFFF) << 32) | (uint64_t)htonl((uint64_t)(x) >> 32))
#endif
#ifndef ntohll
#define ntohll(x) htonll(x)
#endif
namespace rtc { namespace rtc {
@ -45,63 +31,40 @@ typedef uint32_t SSRC;
#pragma pack(push, 1) #pragma pack(push, 1)
struct RTP { struct RTC_CPP_EXPORT RTP {
private:
uint8_t _first; uint8_t _first;
uint8_t _payloadType; uint8_t _payloadType;
uint16_t _seqNumber; uint16_t _seqNumber;
uint32_t _timestamp; uint32_t _timestamp;
SSRC _ssrc; SSRC _ssrc;
SSRC _csrc[16];
public: [[nodiscard]] uint8_t version() const;
SSRC csrc[16]; [[nodiscard]] bool padding() const;
[[nodiscard]] bool extension() const;
[[nodiscard]] uint8_t csrcCount() const;
[[nodiscard]] uint8_t marker() const;
[[nodiscard]] uint8_t payloadType() const;
[[nodiscard]] uint16_t seqNumber() const;
[[nodiscard]] uint32_t timestamp() const;
[[nodiscard]] uint32_t ssrc() const;
inline uint8_t version() const { return _first >> 6; } [[nodiscard]] size_t getSize() const;
inline bool padding() const { return (_first >> 5) & 0x01; } [[nodiscard]] const char *getBody() const;
inline bool extension() const { return (_first >> 4) & 0x01; } [[nodiscard]] char *getBody();
inline uint8_t csrcCount() const { return _first & 0x0F; }
inline uint8_t marker() const { return _payloadType & 0b10000000; }
inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
inline uint16_t seqNumber() const { return ntohs(_seqNumber); }
inline uint32_t timestamp() const { return ntohl(_timestamp); }
inline uint32_t ssrc() const { return ntohl(_ssrc); }
inline size_t getSize() const { void log() const;
return reinterpret_cast<const char *>(&csrc) - reinterpret_cast<const char *>(this) +
sizeof(SSRC) * csrcCount();
}
[[nodiscard]] char *getBody() { void preparePacket();
return reinterpret_cast<char *>(&csrc) + sizeof(SSRC) * csrcCount(); void setSeqNumber(uint16_t newSeqNo);
} void setPayloadType(uint8_t newPayloadType);
void setSsrc(uint32_t in_ssrc);
[[nodiscard]] const char *getBody() const { void setMarker(bool marker);
return reinterpret_cast<const char *>(&csrc) + sizeof(SSRC) * csrcCount(); void setTimestamp(uint32_t i);
}
inline void preparePacket() { _first |= (1 << 7); }
inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
inline void setPayloadType(uint8_t newPayloadType) {
_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
}
inline void setSsrc(uint32_t in_ssrc) { _ssrc = htonl(in_ssrc); }
inline void setMarker(bool marker) { _payloadType = (_payloadType & 0x7F) | (marker << 7); };
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
void log() {
PLOG_VERBOSE << "RTP V: " << (int)version() << " P: " << (padding() ? "P" : " ")
<< " X: " << (extension() ? "X" : " ") << " CC: " << (int)csrcCount()
<< " M: " << (marker() ? "M" : " ") << " PT: " << (int)payloadType()
<< " SEQNO: " << seqNumber() << " TS: " << timestamp();
}
}; };
struct RTCP_ReportBlock { struct RTC_CPP_EXPORT RTCP_ReportBlock {
SSRC ssrc; SSRC _ssrc;
private:
uint32_t _fractionLostAndPacketsLost; // fraction lost is 8-bit, packets lost is 24-bit uint32_t _fractionLostAndPacketsLost; // fraction lost is 8-bit, packets lost is 24-bit
uint16_t _seqNoCycles; uint16_t _seqNoCycles;
uint16_t _highestSeqNo; uint16_t _highestSeqNo;
@ -109,136 +72,68 @@ private:
uint32_t _lastReport; uint32_t _lastReport;
uint32_t _delaySinceLastReport; uint32_t _delaySinceLastReport;
public: [[nodiscard]] uint16_t seqNoCycles() const;
inline void preparePacket(SSRC in_ssrc, [[maybe_unused]] unsigned int packetsLost, [[nodiscard]] uint16_t highestSeqNo() const;
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo, [[nodiscard]] uint32_t jitter() const;
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP, [[nodiscard]] uint32_t delaySinceSR() const;
uint64_t lastSR_DELAY) {
setSeqNo(highestSeqNo, seqNoCycles);
setJitter(jitter);
setSSRC(in_ssrc);
// Middle 32 bits of NTP Timestamp [[nodiscard]] SSRC getSSRC() const;
// this->lastReport = lastSR_NTP >> 16u; [[nodiscard]] uint32_t getNTPOfSR() const;
setNTPOfSR(uint64_t(lastSR_NTP)); [[nodiscard]] unsigned int getLossPercentage() const;
setDelaySinceSR(uint32_t(lastSR_DELAY)); [[nodiscard]] unsigned int getPacketLostCount() const;
// The delay, expressed in units of 1/65536 seconds void preparePacket(SSRC in_ssrc, unsigned int packetsLost, unsigned int totalPackets,
// this->delaySinceLastReport = lastSR_DELAY; uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter,
} uint64_t lastSR_NTP, uint64_t lastSR_DELAY);
void setSSRC(SSRC in_ssrc);
void setPacketsLost(unsigned int packetsLost, unsigned int totalPackets);
void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles);
void setJitter(uint32_t jitter);
void setNTPOfSR(uint64_t ntp);
void setDelaySinceSR(uint32_t sr);
inline void setSSRC(SSRC in_ssrc) { this->ssrc = htonl(in_ssrc); } void log() const;
[[nodiscard]] inline SSRC getSSRC() const { return ntohl(ssrc); }
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets) {
// TODO Implement loss percentages.
_fractionLostAndPacketsLost = 0;
}
[[nodiscard]] inline unsigned int getLossPercentage() const {
// TODO Implement loss percentages.
return 0;
}
[[nodiscard]] inline unsigned int getPacketLostCount() const {
// TODO Implement total packets lost.
return 0;
}
inline uint16_t seqNoCycles() const { return ntohs(_seqNoCycles); }
inline uint16_t highestSeqNo() const { return ntohs(_highestSeqNo); }
inline uint32_t jitter() const { return ntohl(_jitter); }
inline void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
_highestSeqNo = htons(highestSeqNo);
_seqNoCycles = htons(seqNoCycles);
}
inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
[[nodiscard]] inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
inline void setDelaySinceSR(uint32_t sr) {
// The delay, expressed in units of 1/65536 seconds
_delaySinceLastReport = htonl(sr);
}
[[nodiscard]] inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
inline void log() const {
PLOG_VERBOSE << "RTCP report block: "
<< "ssrc="
<< ntohl(ssrc)
// TODO: Implement these reports
// << ", fractionLost=" << fractionLost
// << ", packetsLost=" << packetsLost
<< ", highestSeqNo=" << highestSeqNo() << ", seqNoCycles=" << seqNoCycles()
<< ", jitter=" << jitter() << ", lastSR=" << getNTPOfSR()
<< ", lastSRDelay=" << getDelaySinceSR();
}
}; };
struct RTCP_HEADER { struct RTC_CPP_EXPORT RTCP_HEADER {
private:
uint8_t _first; uint8_t _first;
uint8_t _payloadType; uint8_t _payloadType;
uint16_t _length; uint16_t _length;
public: [[nodiscard]] uint8_t version() const;
inline uint8_t version() const { return _first >> 6; } [[nodiscard]] bool padding() const;
inline bool padding() const { return (_first >> 5) & 0x01; } [[nodiscard]] uint8_t reportCount() const;
inline uint8_t reportCount() const { return _first & 0x0F; } [[nodiscard]] uint8_t payloadType() const;
inline uint8_t payloadType() const { return _payloadType; } [[nodiscard]] uint16_t length() const;
inline uint16_t length() const { return ntohs(_length); } [[nodiscard]] size_t lengthInBytes() const;
inline size_t lengthInBytes() const { return (1 + length()) * 4; }
inline void setPayloadType(uint8_t type) { _payloadType = type; } void prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length);
inline void setReportCount(uint8_t count) { void setPayloadType(uint8_t type);
_first = (_first & 0b11100000u) | (count & 0b00011111u); void setReportCount(uint8_t count);
} void setLength(uint16_t length);
inline void setLength(uint16_t length) { _length = htons(length); }
inline void prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length) { void log() const;
_first = 0b10000000; // version 2, no padding
setReportCount(reportCount);
setPayloadType(payloadType);
setLength(length);
}
inline void log() const {
PLOG_VERBOSE << "RTCP header: "
<< "version=" << unsigned(version()) << ", padding=" << padding()
<< ", reportCount=" << unsigned(reportCount())
<< ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
}
}; };
struct RTCP_FB_HEADER { struct RTC_CPP_EXPORT RTCP_FB_HEADER {
RTCP_HEADER header; RTCP_HEADER header;
SSRC packetSender;
SSRC mediaSource;
[[nodiscard]] SSRC getPacketSenderSSRC() const { return ntohl(packetSender); } SSRC _packetSender;
SSRC _mediaSource;
[[nodiscard]] SSRC getMediaSourceSSRC() const { return ntohl(mediaSource); } [[nodiscard]] SSRC packetSenderSSRC() const;
[[nodiscard]] SSRC mediaSourceSSRC() const;
void setPacketSenderSSRC(SSRC ssrc) { this->packetSender = htonl(ssrc); } void setPacketSenderSSRC(SSRC ssrc);
void setMediaSourceSSRC(SSRC ssrc);
void setMediaSourceSSRC(SSRC ssrc) { this->mediaSource = htonl(ssrc); } void log() const;
void log() {
header.log();
PLOG_VERBOSE << "FB: "
<< " packet sender: " << getPacketSenderSSRC()
<< " media source: " << getMediaSourceSSRC();
}
}; };
struct RTCP_SR { struct RTC_CPP_EXPORT RTCP_SR {
RTCP_HEADER header; RTCP_HEADER header;
SSRC _senderSSRC; SSRC _senderSSRC;
private:
uint64_t _ntpTimestamp; uint64_t _ntpTimestamp;
uint32_t _rtpTimestamp; uint32_t _rtpTimestamp;
uint32_t _packetCount; uint32_t _packetCount;
@ -246,418 +141,175 @@ private:
RTCP_ReportBlock _reportBlocks; RTCP_ReportBlock _reportBlocks;
public: [[nodiscard]] static unsigned int Size(unsigned int reportCount);
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
unsigned int length =
((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
header.prepareHeader(200, reportCount, uint16_t(length));
this->_senderSSRC = htonl(senderSSRC);
}
[[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; } [[nodiscard]] uint64_t ntpTimestamp() const;
[[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const { [[nodiscard]] uint32_t rtpTimestamp() const;
return &_reportBlocks + num; [[nodiscard]] uint32_t packetCount() const;
} [[nodiscard]] uint32_t octetCount() const;
[[nodiscard]] uint32_t senderSSRC() const;
[[nodiscard]] static unsigned int size(unsigned int reportCount) { [[nodiscard]] const RTCP_ReportBlock *getReportBlock(int num) const;
return sizeof(RTCP_HEADER) + 24 + reportCount * sizeof(RTCP_ReportBlock); [[nodiscard]] RTCP_ReportBlock *getReportBlock(int num);
} [[nodiscard]] unsigned int size(unsigned int reportCount);
[[nodiscard]] size_t getSize() const;
[[nodiscard]] inline size_t getSize() const { void preparePacket(SSRC senderSSRC, uint8_t reportCount);
// "length" in packet is one less than the number of 32 bit words in the packet. void setNtpTimestamp(uint64_t ts);
return sizeof(uint32_t) * (1 + size_t(header.length())); void setRtpTimestamp(uint32_t ts);
} void setOctetCount(uint32_t ts);
void setPacketCount(uint32_t ts);
inline uint64_t ntpTimestamp() const { return ntohll(_ntpTimestamp); } void log() const;
inline uint32_t rtpTimestamp() const { return ntohl(_rtpTimestamp); }
inline uint32_t packetCount() const { return ntohl(_packetCount); }
inline uint32_t octetCount() const { return ntohl(_octetCount); }
inline uint32_t senderSSRC() const { return ntohl(_senderSSRC); }
inline void setNtpTimestamp(uint64_t ts) { _ntpTimestamp = htonll(ts); }
inline void setRtpTimestamp(uint32_t ts) { _rtpTimestamp = htonl(ts); }
inline void setOctetCount(uint32_t ts) { _octetCount = htonl(ts); }
inline void setPacketCount(uint32_t ts) { _packetCount = htonl(ts); }
inline void log() const {
header.log();
PLOG_VERBOSE << "RTCP SR: "
<< " SSRC=" << senderSSRC() << ", NTP_TS=" << ntpTimestamp()
<< ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
<< ", octetCount=" << octetCount();
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
getReportBlock(i)->log();
}
}
}; };
struct RTCP_SDES_ITEM { struct RTC_CPP_EXPORT RTCP_SDES_ITEM {
public:
uint8_t type; uint8_t type;
private:
uint8_t _length; uint8_t _length;
char _text[1]; char _text[1];
public: [[nodiscard]] static unsigned int Size(uint8_t textLength);
inline std::string text() const { return std::string(_text, _length); }
inline void setText(std::string text) {
if(text.size() > 0xFF)
throw std::invalid_argument("text is too long");
_length = uint8_t(text.size()); [[nodiscard]] string text() const;
memcpy(_text, text.data(), text.size()); [[nodiscard]] uint8_t length() const;
}
inline uint8_t length() { return _length; } void setText(string text);
[[nodiscard]] static unsigned int size(uint8_t textLength) { return textLength + 2; }
}; };
struct RTCP_SDES_CHUNK { struct RTCP_SDES_CHUNK {
private:
SSRC _ssrc; SSRC _ssrc;
RTCP_SDES_ITEM _items; RTCP_SDES_ITEM _items;
public: [[nodiscard]] static unsigned int Size(const std::vector<uint8_t> textLengths);
inline SSRC ssrc() const { return ntohl(_ssrc); }
inline void setSSRC(SSRC ssrc) { _ssrc = htonl(ssrc); }
/// Get item at given index [[nodiscard]] SSRC ssrc() const;
/// @note All items with index < `num` must be valid, otherwise this function has undefined
/// behaviour (use `safelyCountChunkSize` to check if chunk is valid)
/// @param num Index of item to return
inline RTCP_SDES_ITEM *getItem(int num) {
auto base = &_items;
while (num-- > 0) {
auto itemSize = RTCP_SDES_ITEM::size(base->length());
base = reinterpret_cast<RTCP_SDES_ITEM *>(reinterpret_cast<uint8_t *>(base) + itemSize);
}
return reinterpret_cast<RTCP_SDES_ITEM *>(base);
}
long safelyCountChunkSize(size_t maxChunkSize) { void setSSRC(SSRC ssrc);
if (maxChunkSize < RTCP_SDES_CHUNK::size({})) {
// chunk is truncated
return -1;
} else {
size_t size = sizeof(SSRC);
unsigned int i = 0;
// We can always access first 4 bytes of first item (in case of no items there will be 4
// null bytes)
auto item = getItem(i);
std::vector<uint8_t> textsLength{};
while (item->type != 0) {
if (size + RTCP_SDES_ITEM::size(0) > maxChunkSize) {
// item is too short
return -1;
}
auto itemLength = item->length();
if (size + RTCP_SDES_ITEM::size(itemLength) >= maxChunkSize) {
// item is too large (it can't be equal to chunk size because after item there
// must be 1-4 null bytes as padding)
return -1;
}
textsLength.push_back(itemLength);
// safely to access next item
item = getItem(++i);
}
auto realSize = RTCP_SDES_CHUNK::size(textsLength);
if (realSize > maxChunkSize) {
// Chunk is too large
return -1;
}
return realSize;
}
}
[[nodiscard]] static unsigned int size(const std::vector<uint8_t> textLengths) { // Get item at given index
unsigned int itemsSize = 0; // All items with index < num must be valid, otherwise this function has undefined behaviour
for (auto length : textLengths) { // (use safelyCountChunkSize() to check if chunk is valid).
itemsSize += RTCP_SDES_ITEM::size(length); [[nodiscard]] const RTCP_SDES_ITEM *getItem(int num) const;
} [[nodiscard]] RTCP_SDES_ITEM *getItem(int num);
auto nullTerminatedItemsSize = itemsSize + 1;
auto words = uint8_t(std::ceil(double(nullTerminatedItemsSize) / 4)) + 1;
return words * 4;
}
/// Get size of chunk // Get size of chunk
/// @note All items must be valid, otherwise this function has undefined behaviour (use // All items must be valid, otherwise this function has undefined behaviour (use
/// `safelyCountChunkSize` to check if chunk is valid) // safelyCountChunkSize() to check if chunk is valid)
[[nodiscard]] unsigned int getSize() { [[nodiscard]] unsigned int getSize() const;
std::vector<uint8_t> textLengths{};
unsigned int i = 0; long safelyCountChunkSize(size_t maxChunkSize) const;
auto item = getItem(i);
while (item->type != 0) {
textLengths.push_back(item->length());
item = getItem(++i);
}
return size(textLengths);
}
}; };
struct RTCP_SDES { struct RTC_CPP_EXPORT RTCP_SDES {
RTCP_HEADER header; RTCP_HEADER header;
private:
RTCP_SDES_CHUNK _chunks; RTCP_SDES_CHUNK _chunks;
public: [[nodiscard]] static unsigned int Size(const std::vector<std::vector<uint8_t>> lengths);
inline void preparePacket(uint8_t chunkCount) {
unsigned int chunkSize = 0;
for (uint8_t i = 0; i < chunkCount; i++) {
auto chunk = getChunk(i);
chunkSize += chunk->getSize();
}
uint16_t length = uint16_t((sizeof(header) + chunkSize) / 4 - 1);
header.prepareHeader(202, chunkCount, length);
}
bool isValid() { bool isValid() const;
auto chunksSize = header.lengthInBytes() - sizeof(header);
if (chunksSize == 0) {
return true;
} else {
// there is at least one chunk
unsigned int i = 0;
unsigned int size = 0;
while (size < chunksSize) {
if (chunksSize < size + RTCP_SDES_CHUNK::size({})) {
// chunk is truncated
return false;
}
auto chunk = getChunk(i++);
auto chunkSize = chunk->safelyCountChunkSize(chunksSize - size);
if (chunkSize < 0) {
// chunk is invalid
return false;
}
size += chunkSize;
}
return size == chunksSize;
}
}
/// Returns number of chunks in this packet // Returns number of chunks in this packet
/// @note Returns 0 if packet is invalid // Returns 0 if packet is invalid
inline unsigned int chunksCount() { unsigned int chunksCount() const;
if (!isValid()) {
return 0;
}
uint16_t chunksSize = 4 * (header.length() + 1) - sizeof(header);
unsigned int size = 0;
unsigned int i = 0;
while (size < chunksSize) {
size += getChunk(i++)->getSize();
}
return i;
}
/// Get chunk at given index // Get chunk at given index
/// @note All chunks (and their items) with index < `num` must be valid, otherwise this function // All chunks (and their items) with index < `num` must be valid, otherwise this function has
/// has undefined behaviour (use `isValid` to check if chunk is valid) // undefined behaviour (use `isValid` to check if chunk is valid).
/// @param num Index of chunk to return const RTCP_SDES_CHUNK *getChunk(int num) const;
inline RTCP_SDES_CHUNK *getChunk(int num) { RTCP_SDES_CHUNK *getChunk(int num);
auto base = &_chunks;
while (num-- > 0) {
auto chunkSize = base->getSize();
base =
reinterpret_cast<RTCP_SDES_CHUNK *>(reinterpret_cast<uint8_t *>(base) + chunkSize);
}
return reinterpret_cast<RTCP_SDES_CHUNK *>(base);
}
[[nodiscard]] static unsigned int size(const std::vector<std::vector<uint8_t>> lengths) { void preparePacket(uint8_t chunkCount);
unsigned int chunks_size = 0;
for (auto length : lengths) {
chunks_size += RTCP_SDES_CHUNK::size(length);
}
return 4 + chunks_size;
}
}; };
struct RTCP_RR { struct RTC_CPP_EXPORT RTCP_RR {
RTCP_HEADER header; RTCP_HEADER header;
SSRC _senderSSRC;
private: SSRC _senderSSRC;
RTCP_ReportBlock _reportBlocks; RTCP_ReportBlock _reportBlocks;
public: [[nodiscard]] static size_t SizeWithReportBlocks(uint8_t reportCount);
[[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
[[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const {
return &_reportBlocks + num;
}
inline SSRC senderSSRC() const { return ntohl(_senderSSRC); } SSRC senderSSRC() const;
inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); } bool isSenderReport();
bool isReceiverReport();
[[nodiscard]] inline size_t getSize() const { [[nodiscard]] RTCP_ReportBlock *getReportBlock(int num);
// "length" in packet is one less than the number of 32 bit words in the packet. [[nodiscard]] const RTCP_ReportBlock *getReportBlock(int num) const;
return sizeof(uint32_t) * (1 + size_t(header.length())); [[nodiscard]] size_t getSize() const;
}
inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) { void preparePacket(SSRC senderSSRC, uint8_t reportCount);
// "length" in packet is one less than the number of 32 bit words in the packet. void setSenderSSRC(SSRC ssrc);
size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1;
header.prepareHeader(201, reportCount, uint16_t(length));
this->_senderSSRC = htonl(senderSSRC);
}
inline static size_t sizeWithReportBlocks(uint8_t reportCount) { void log() const;
return sizeof(header) + 4 + size_t(reportCount) * sizeof(RTCP_ReportBlock);
}
inline bool isSenderReport() { return header.payloadType() == 200; }
inline bool isReceiverReport() { return header.payloadType() == 201; }
inline void log() const {
header.log();
PLOG_VERBOSE << "RTCP RR: "
<< " SSRC=" << ntohl(_senderSSRC);
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
getReportBlock(i)->log();
}
}
}; };
struct RTCP_REMB { struct RTC_CPP_EXPORT RTCP_REMB {
RTCP_FB_HEADER header; RTCP_FB_HEADER header;
/*! \brief Unique identifier ('R' 'E' 'M' 'B') */ char _id[4]; // Unique identifier ('R' 'E' 'M' 'B')
char id[4]; uint32_t _bitrate; // Num SSRC, Br Exp, Br Mantissa (bit mask)
SSRC _ssrc[1];
/*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */ [[nodiscard]] static size_t SizeWithSSRCs(int count);
uint32_t bitrate;
SSRC ssrc[1]; [[nodiscard]] unsigned int getSize() const;
[[nodiscard]] unsigned int getSize() const { void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate);
// "length" in packet is one less than the number of 32 bit words in the packet. void setBitrate(unsigned int numSSRC, unsigned int in_bitrate);
return sizeof(uint32_t) * (1 + header.header.length()); void setSsrc(int iterator, SSRC newSssrc);
}
void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate) {
// Report Count becomes the format here.
header.header.prepareHeader(206, 15, 0);
// Always zero.
header.setMediaSourceSSRC(0);
header.setPacketSenderSSRC(senderSSRC);
id[0] = 'R';
id[1] = 'E';
id[2] = 'M';
id[3] = 'B';
setBitrate(numSSRC, in_bitrate);
}
void setBitrate(unsigned int numSSRC, unsigned int in_bitrate) {
unsigned int exp = 0;
while (in_bitrate > pow(2, 18) - 1) {
exp++;
in_bitrate /= 2;
}
// "length" in packet is one less than the number of 32 bit words in the packet.
header.header.setLength(
uint16_t((offsetof(RTCP_REMB, ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | in_bitrate);
}
void setSsrc(int iterator, SSRC newSssrc) { ssrc[iterator] = htonl(newSssrc); }
size_t static inline sizeWithSSRCs(int count) {
return sizeof(RTCP_REMB) + (count - 1) * sizeof(SSRC);
}
}; };
struct RTCP_PLI { struct RTC_CPP_EXPORT RTCP_PLI {
RTCP_FB_HEADER header; RTCP_FB_HEADER header;
void preparePacket(SSRC messageSSRC) { [[nodiscard]] static unsigned int Size();
header.header.prepareHeader(206, 1, 2);
header.setPacketSenderSSRC(messageSSRC);
header.setMediaSourceSSRC(messageSSRC);
}
void print() { header.log(); } void preparePacket(SSRC messageSSRC);
[[nodiscard]] static unsigned int size() { return sizeof(RTCP_FB_HEADER); } void log() const;
}; };
struct RTCP_FIR_PART { struct RTC_CPP_EXPORT RTCP_FIR_PART {
uint32_t ssrc; uint32_t ssrc;
uint8_t seqNo; uint8_t seqNo;
uint8_t dummy1; uint8_t dummy1;
uint16_t dummy2; uint16_t dummy2;
}; };
struct RTCP_FIR { struct RTC_CPP_EXPORT RTCP_FIR {
RTCP_FB_HEADER header; RTCP_FB_HEADER header;
RTCP_FIR_PART parts[1]; RTCP_FIR_PART parts[1];
void preparePacket(SSRC messageSSRC, uint8_t seqNo) { static unsigned int Size();
header.header.prepareHeader(206, 4, 2 + 2 * 1);
header.setPacketSenderSSRC(messageSSRC);
header.setMediaSourceSSRC(messageSSRC);
parts[0].ssrc = htonl(messageSSRC);
parts[0].seqNo = seqNo;
}
void print() { header.log(); } void preparePacket(SSRC messageSSRC, uint8_t seqNo);
[[nodiscard]] static unsigned int size() { void log() const;
return sizeof(RTCP_FB_HEADER) + sizeof(RTCP_FIR_PART);
}
}; };
struct RTCP_NACK_PART { struct RTC_CPP_EXPORT RTCP_NACK_PART {
uint16_t _pid; uint16_t _pid;
uint16_t _blp; uint16_t _blp;
uint16_t getPID() { return ntohs(_pid); } uint16_t pid();
uint16_t getBLP() { return ntohs(_blp); } uint16_t blp();
void setPID(uint16_t pid) { _pid = htons(pid); } void setPid(uint16_t pid);
void setBLP(uint16_t blp) { _blp = htons(blp); } void setBlp(uint16_t blp);
std::vector<uint16_t> getSequenceNumbers() { std::vector<uint16_t> getSequenceNumbers();
std::vector<uint16_t> result{};
result.reserve(17);
uint16_t pid = getPID();
result.push_back(pid);
uint16_t bitmask = getBLP();
uint16_t i = pid + 1;
while (bitmask > 0) {
if (bitmask & 0x1) {
result.push_back(i);
}
i += 1;
bitmask >>= 1;
}
return result;
}
}; };
class RTCP_NACK { struct RTC_CPP_EXPORT RTCP_NACK {
public:
RTCP_FB_HEADER header; RTCP_FB_HEADER header;
RTCP_NACK_PART parts[1]; RTCP_NACK_PART parts[1];
public: [[nodiscard]] static unsigned int Size(unsigned int discreteSeqNoCount);
void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
header.header.prepareHeader(205, 1, 2 + uint16_t(discreteSeqNoCount)); [[nodiscard]] unsigned int getSeqNoCount();
header.setMediaSourceSSRC(ssrc);
header.setPacketSenderSSRC(ssrc); void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount);
}
/** /**
* Add a packet to the list of missing packets. * Add a packet to the list of missing packets.
@ -668,71 +320,22 @@ public:
* @param missingPacket The seq no of the missing packet. This will be added to the queue. * @param missingPacket The seq no of the missing packet. This will be added to the queue.
* @return true if the packet has grown, false otherwise. * @return true if the packet has grown, false otherwise.
*/ */
bool addMissingPacket(unsigned int *fciCount, uint16_t *fciPID, uint16_t missingPacket) { bool addMissingPacket(unsigned int *fciCount, uint16_t *fciPID, uint16_t missingPacket);
if (*fciCount == 0 || missingPacket < *fciPID || missingPacket > (*fciPID + 16)) {
parts[*fciCount].setPID(missingPacket);
parts[*fciCount].setBLP(0);
*fciPID = missingPacket;
(*fciCount)++;
return true;
} else {
// TODO SPEED!
uint16_t blp = parts[(*fciCount) - 1].getBLP();
uint16_t newBit = uint16_t(1u << (missingPacket - (1 + *fciPID)));
parts[(*fciCount) - 1].setBLP(blp | newBit);
return false;
}
}
[[nodiscard]] static unsigned int getSize(unsigned int discreteSeqNoCount) {
return offsetof(RTCP_NACK, parts) + sizeof(RTCP_NACK_PART) * discreteSeqNoCount;
}
[[nodiscard]] unsigned int getSeqNoCount() { return header.header.length() - 2; }
}; };
class RTP_RTX { struct RTC_CPP_EXPORT RTP_RTX {
private:
RTP header; RTP header;
public: [[nodiscard]] const char *getBody() const;
size_t copyTo(RTP *dest, size_t totalSize, uint8_t originalPayloadType) { [[nodiscard]] char *getBody();
memmove((char *)dest, (char *)this, header.getSize()); [[nodiscard]] size_t getBodySize(size_t totalSize) const;
dest->setSeqNumber(getOriginalSeqNo()); [[nodiscard]] size_t getSize() const;
dest->setPayloadType(originalPayloadType); [[nodiscard]] uint16_t getOriginalSeqNo() const;
memmove(dest->getBody(), getBody(), getBodySize(totalSize));
return totalSize;
}
[[nodiscard]] uint16_t getOriginalSeqNo() const { // Returns the new size of the packet
return ntohs(*(uint16_t *)(header.getBody())); size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType);
}
[[nodiscard]] char *getBody() { return header.getBody() + sizeof(uint16_t); } size_t copyTo(RTP *dest, size_t totalSize, uint8_t originalPayloadType);
[[nodiscard]] const char *getBody() const { return header.getBody() + sizeof(uint16_t); }
[[nodiscard]] size_t getBodySize(size_t totalSize) {
return totalSize - (getBody() - reinterpret_cast<char *>(this));
}
[[nodiscard]] size_t getSize() const{
return header.getSize() + sizeof(uint16_t);
}
[[nodiscard]] RTP &getHeader() { return header; }
/*
* Returns the new size of the packet
*/
size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
header.setSeqNumber(getOriginalSeqNo());
header.setSsrc(originalSSRC);
header.setPayloadType(originalPayloadType);
// TODO, the -12 is the size of the header (which is variable!)
memmove(header.getBody(), getBody(), totalSize - getSize());
return totalSize - 2;
}
}; };
#pragma pack(pop) #pragma pack(pop)

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_RTP_PACKETIZATION_CONFIG_H #ifndef RTC_RTP_PACKETIZATION_CONFIG_H

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_RTP_PACKETIZER_H #ifndef RTC_RTP_PACKETIZER_H

View File

@ -22,6 +22,9 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional>
#include <tuple>
#include <utility>
namespace rtc { namespace rtc {
@ -32,11 +35,10 @@ template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
// weak_ptr bind helper // weak_ptr bind helper
template <typename F, typename T, typename... Args> auto weak_bind(F &&f, T *t, Args &&..._args) { template <typename F, typename T, typename... Args> auto weak_bind(F &&f, T *t, Args &&..._args) {
return [bound = std::bind(f, t, _args...), weak_this = t->weak_from_this()](auto &&...args) { return [bound = std::bind(f, t, _args...), weak_this = t->weak_from_this()](auto &&...args) {
using result_type = typename decltype(bound)::result_type;
if (auto shared_this = weak_this.lock()) if (auto shared_this = weak_this.lock())
return bound(args...); return bound(args...);
else else
return static_cast<result_type>(false); return static_cast<decltype(bound(args...))>(false);
}; };
} }
@ -58,40 +60,35 @@ private:
}; };
// callback with built-in synchronization // callback with built-in synchronization
template <typename... Args> class synchronized_callback final { template <typename... Args> class synchronized_callback {
public: public:
synchronized_callback() = default; synchronized_callback() = default;
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); } synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
synchronized_callback(const synchronized_callback &cb) { *this = cb; } synchronized_callback(const synchronized_callback &cb) { *this = cb; }
synchronized_callback(std::function<void(Args...)> func) { *this = std::move(func); } synchronized_callback(std::function<void(Args...)> func) { *this = std::move(func); }
~synchronized_callback() { *this = nullptr; } virtual ~synchronized_callback() { *this = nullptr; }
synchronized_callback &operator=(synchronized_callback &&cb) { synchronized_callback &operator=(synchronized_callback &&cb) {
std::scoped_lock lock(mutex, cb.mutex); std::scoped_lock lock(mutex, cb.mutex);
callback = std::move(cb.callback); set(std::exchange(cb.callback, nullptr));
cb.callback = nullptr;
return *this; return *this;
} }
synchronized_callback &operator=(const synchronized_callback &cb) { synchronized_callback &operator=(const synchronized_callback &cb) {
std::scoped_lock lock(mutex, cb.mutex); std::scoped_lock lock(mutex, cb.mutex);
callback = cb.callback; set(cb.callback);
return *this; return *this;
} }
synchronized_callback &operator=(std::function<void(Args...)> func) { synchronized_callback &operator=(std::function<void(Args...)> func) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
callback = std::move(func); set(std::move(func));
return *this; return *this;
} }
bool operator()(Args... args) const { bool operator()(Args... args) const {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
if (!callback) return call(std::move(args)...);
return false;
callback(std::move(args)...);
return true;
} }
operator bool() const { operator bool() const {
@ -103,11 +100,48 @@ public:
return [this](Args... args) { (*this)(std::move(args)...); }; return [this](Args... args) { (*this)(std::move(args)...); };
} }
private: protected:
virtual void set(std::function<void(Args...)> func) { callback = std::move(func); }
virtual bool call(Args... args) const {
if (!callback)
return false;
callback(std::move(args)...);
return true;
}
std::function<void(Args...)> callback; std::function<void(Args...)> callback;
mutable std::recursive_mutex mutex; mutable std::recursive_mutex mutex;
}; };
// callback with built-in synchronization and replay of the last missed call
template <typename... Args>
class synchronized_stored_callback final : public synchronized_callback<Args...> {
public:
template <typename... CArgs>
synchronized_stored_callback(CArgs &&...cargs)
: synchronized_callback<Args...>(std::forward<CArgs>(cargs)...) {}
~synchronized_stored_callback() {}
private:
void set(std::function<void(Args...)> func) {
synchronized_callback<Args...>::set(func);
if (func && stored) {
std::apply(func, std::move(*stored));
stored.reset();
}
}
bool call(Args... args) const {
if (!synchronized_callback<Args...>::call(args...))
stored.emplace(std::move(args)...);
return true;
}
mutable std::optional<std::tuple<Args...>> stored;
};
// pimpl base class // pimpl base class
template <typename T> using impl_ptr = std::shared_ptr<T>; template <typename T> using impl_ptr = std::shared_ptr<T>;
template <typename T> class CheshireCat { template <typename T> class CheshireCat {

View File

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

View File

@ -17,10 +17,9 @@
*/ */
#include "rtc.h" #include "rtc.h"
#include "rtc.hpp" #include "rtc.hpp"
#include "plog/Formatters/FuncMessageFormatter.h" #include "impl/internals.hpp"
#include <chrono> #include <chrono>
#include <exception> #include <exception>
@ -29,11 +28,6 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#ifdef _WIN32
#include <codecvt>
#include <locale>
#endif
using namespace rtc; using namespace rtc;
using std::chrono::milliseconds; using std::chrono::milliseconds;
@ -146,7 +140,7 @@ shared_ptr<RtcpSrReporter> getRtcpSrReporter(int id) {
if (auto it = rtcpSrReporterMap.find(id); it != rtcpSrReporterMap.end()) { if (auto it = rtcpSrReporterMap.find(id); it != rtcpSrReporterMap.end()) {
return it->second; return it->second;
} else { } else {
throw std::invalid_argument("RtcpSRReporter ID does not exist"); throw std::invalid_argument("RTCP SR reporter ID does not exist");
} }
} }
@ -160,7 +154,7 @@ shared_ptr<MediaChainableHandler> getMediaChainableHandler(int id) {
if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) { if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) {
return it->second; return it->second;
} else { } else {
throw std::invalid_argument("RtcpChainableHandler ID does not exist"); throw std::invalid_argument("RTCP chainable handler ID does not exist");
} }
} }
@ -169,44 +163,31 @@ void emplaceMediaChainableHandler(shared_ptr<MediaChainableHandler> ptr, int tr)
rtcpChainableHandlerMap.emplace(std::make_pair(tr, ptr)); rtcpChainableHandlerMap.emplace(std::make_pair(tr, ptr));
} }
shared_ptr<RtpPacketizationConfig> getRTPConfig(int id) { shared_ptr<RtpPacketizationConfig> getRtpConfig(int id) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
if (auto it = rtpConfigMap.find(id); it != rtpConfigMap.end()) { if (auto it = rtpConfigMap.find(id); it != rtpConfigMap.end()) {
return it->second; return it->second;
} else { } else {
throw std::invalid_argument("RTPConfiguration ID does not exist"); throw std::invalid_argument("RTP configuration ID does not exist");
} }
} }
void emplaceRTPConfig(shared_ptr<RtpPacketizationConfig> ptr, int tr) { void emplaceRtpConfig(shared_ptr<RtpPacketizationConfig> ptr, int tr) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
rtpConfigMap.emplace(std::make_pair(tr, ptr)); rtpConfigMap.emplace(std::make_pair(tr, ptr));
} }
Description::Direction rtcDirectionToDirection(rtcDirection direction) {
switch (direction) {
case RTC_DIRECTION_SENDONLY:
return Description::Direction::SendOnly;
case RTC_DIRECTION_RECVONLY:
return Description::Direction::RecvOnly;
case RTC_DIRECTION_SENDRECV:
return Description::Direction::SendRecv;
case RTC_DIRECTION_INACTIVE:
return Description::Direction::Inactive;
default:
return Description::Direction::Unknown;
}
}
shared_ptr<RtpPacketizationConfig> shared_ptr<RtpPacketizationConfig>
getNewRtpPacketizationConfig(uint32_t ssrc, const char *cname, uint8_t payloadType, createRtpPacketizationConfig(const rtcPacketizationHandlerInit *init) {
uint32_t clockRate, uint16_t sequenceNumber, uint32_t timestamp) { if (!init)
if (!cname) { throw std::invalid_argument("Unexpected null pointer for packetization handler init");
throw std::invalid_argument("Unexpected null pointer for cname");
}
return std::make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, clockRate, if (!init->cname)
sequenceNumber, timestamp); 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 #endif // RTC_ENABLE_MEDIA
@ -287,58 +268,16 @@ int copyAndReturn(binary b, char *buffer, int size) {
return int(b.size()); return int(b.size());
} }
class plogAppender : public plog::IAppender {
public:
plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
plogAppender(plogAppender &&appender) : callback(nullptr) {
std::lock_guard lock(appender.callbackMutex);
std::swap(appender.callback, callback);
}
void setCallback(rtcLogCallbackFunc cb) {
std::lock_guard lock(callbackMutex);
callback = cb;
}
void write(const plog::Record &record) override {
plog::Severity severity = record.getSeverity();
auto formatted = plog::FuncMessageFormatter::format(record);
formatted.pop_back(); // remove newline
#ifdef _WIN32
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
std::string str = converter.to_bytes(formatted);
#else
std::string str = formatted;
#endif
std::lock_guard lock(callbackMutex);
if (callback)
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
else
std::cout << plog::severityToString(severity) << " " << str << std::endl;
}
private:
rtcLogCallbackFunc callback;
std::mutex callbackMutex;
};
} // namespace } // namespace
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) { void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
static optional<plogAppender> appender; LogCallback callback = nullptr;
const auto severity = static_cast<plog::Severity>(level); if (cb)
std::lock_guard lock(mutex); callback = [cb](LogLevel level, string message) {
if (appender) { cb(static_cast<rtcLogLevel>(level), message.c_str());
appender->setCallback(cb); };
InitLogger(severity, nullptr); // change the severity
} else if (cb) { InitLogger(static_cast<LogLevel>(level), callback);
appender.emplace(plogAppender(cb));
InitLogger(severity, &appender.value());
} else {
InitLogger(severity, nullptr); // log to stdout
}
} }
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); } void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
@ -438,39 +377,35 @@ int rtcDeleteDataChannel(int dc) {
}); });
} }
#if RTC_ENABLE_MEDIA int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
return wrap([&] {
if (!mediaDescriptionSdp)
throw std::invalid_argument("Unexpected null pointer for track media description");
void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid, auto peerConnection = getPeerConnection(pc);
const char *_trackID) { Description::Media media{string(mediaDescriptionSdp)};
int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
if (auto ptr = getUserPointer(pc))
rtcSetUserPointer(tr, *ptr);
optional<string> name = nullopt; return tr;
if (_name) { });
name = string(_name);
} }
optional<string> msid = nullopt; int rtcAddTrackEx(int pc, const rtcTrackInit *init) {
if (_msid) {
msid = string(_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,
rtcDirection _direction, const char *_name, const char *_msid,
const char *_trackID) {
return wrap([&] { return wrap([&] {
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
auto direction = rtcDirectionToDirection(_direction); if (!init)
throw std::invalid_argument("Unexpected null pointer for track init");
string mid = "video"; auto direction = static_cast<Description::Direction>(init->direction);
switch (codec) {
string mid;
if (init->mid) {
mid = string(init->mid);
} else {
switch (init->codec) {
case RTC_CODEC_H264: case RTC_CODEC_H264:
case RTC_CODEC_VP8: case RTC_CODEC_VP8:
case RTC_CODEC_VP9: case RTC_CODEC_VP9:
@ -479,28 +414,28 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
case RTC_CODEC_OPUS: case RTC_CODEC_OPUS:
mid = "audio"; mid = "audio";
break; break;
default:
mid = "video";
break;
} }
if (_mid) {
mid = string(_mid);
} }
optional<Description::Media> optDescription = nullopt; optional<Description::Media> optDescription = nullopt;
switch (codec) { switch (init->codec) {
case RTC_CODEC_H264: case RTC_CODEC_H264:
case RTC_CODEC_VP8: case RTC_CODEC_VP8:
case RTC_CODEC_VP9: { case RTC_CODEC_VP9: {
auto desc = Description::Video(mid, direction); auto desc = Description::Video(mid, direction);
switch (codec) { switch (init->codec) {
case RTC_CODEC_H264: case RTC_CODEC_H264:
desc.addH264Codec(payloadType); desc.addH264Codec(init->payloadType);
break; break;
case RTC_CODEC_VP8: case RTC_CODEC_VP8:
desc.addVP8Codec(payloadType); desc.addVP8Codec(init->payloadType);
break; break;
case RTC_CODEC_VP9: case RTC_CODEC_VP9:
desc.addVP8Codec(payloadType); desc.addVP8Codec(init->payloadType);
break; break;
default: default:
break; break;
@ -510,9 +445,9 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
} }
case RTC_CODEC_OPUS: { case RTC_CODEC_OPUS: {
auto desc = Description::Audio(mid, direction); auto desc = Description::Audio(mid, direction);
switch (codec) { switch (init->codec) {
case RTC_CODEC_OPUS: case RTC_CODEC_OPUS:
desc.addOpusCodec(payloadType); desc.addOpusCodec(init->payloadType);
break; break;
default: default:
break; break;
@ -524,168 +459,16 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
break; break;
} }
if (!optDescription.has_value()) { if (!optDescription)
throw std::invalid_argument("Unexpected codec"); throw std::invalid_argument("Unexpected codec");
} else {
auto description = optDescription.value();
setSSRC(&description, ssrc, _name, _msid, _trackID);
int tr = emplaceTrack(peerConnection->addTrack(std::move(description))); auto desc = std::move(*optDescription);
if (auto ptr = getUserPointer(pc)) { desc.addSSRC(init->ssrc, init->name ? std::make_optional(string(init->name)) : nullopt,
rtcSetUserPointer(tr, *ptr); init->msid ? std::make_optional(string(init->msid)) : nullopt,
} init->trackId ? std::make_optional(string(init->trackId)) : nullopt);
return tr;
}
});
}
int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char *cname, uint8_t payloadType, int tr = emplaceTrack(peerConnection->addTrack(std::move(desc)));
uint32_t clockRate, uint16_t maxFragmentSize,
uint16_t sequenceNumber, uint32_t timestamp) {
return wrap([&] {
auto track = getTrack(tr);
// create RTP configuration
auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
sequenceNumber, timestamp);
// create packetizer
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, uint32_t ssrc, const char *cname, uint8_t payloadType,
uint32_t clockRate, uint16_t sequenceNumber,
uint32_t timestamp) {
return wrap([&] {
auto track = getTrack(tr);
// create RTP configuration
auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
sequenceNumber, timestamp);
// 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 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, double startTime_s, bool timeIntervalSince1970,
uint32_t timestamp) {
return wrap([&] {
auto config = getRTPConfig(id);
auto epoch = RtpPacketizationConfig::EpochStart::T1900;
if (timeIntervalSince1970) {
epoch = RtpPacketizationConfig::EpochStart::T1970;
}
config->setStartTime(startTime_s, epoch, 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;
});
}
#endif // RTC_ENABLE_MEDIA
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)) if (auto ptr = getUserPointer(pc))
rtcSetUserPointer(tr, *ptr); rtcSetUserPointer(tr, *ptr);
@ -715,7 +498,141 @@ int rtcGetTrackDescription(int tr, char *buffer, int size) {
}); });
} }
#if RTC_ENABLE_MEDIA
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;
});
}
#endif // RTC_ENABLE_MEDIA
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
int rtcCreateWebSocket(const char *url) { int rtcCreateWebSocket(const char *url) {
return wrap([&] { return wrap([&] {
auto ws = std::make_shared<WebSocket>(); auto ws = std::make_shared<WebSocket>();
@ -748,6 +665,7 @@ int rtcDeleteWebsocket(int ws) {
return RTC_ERR_SUCCESS; return RTC_ERR_SUCCESS;
}); });
} }
#endif #endif
int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) { int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
@ -1189,4 +1107,37 @@ int rtcReceiveMessage(int id, char *buffer, int *size) {
} }
void rtcPreload() { rtc::Preload(); } void rtcPreload() { rtc::Preload(); }
void rtcCleanup() { rtc::Cleanup(); } 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);
SetSctpSettings(std::move(s));
return RTC_ERR_SUCCESS;
});
}

View File

@ -17,8 +17,8 @@
*/ */
#include "channel.hpp" #include "channel.hpp"
#include "globals.hpp"
#include "impl/internals.hpp"
#include "impl/channel.hpp" #include "impl/channel.hpp"
namespace rtc { namespace rtc {
@ -43,10 +43,7 @@ void Channel::onError(std::function<void(string error)> callback) {
void Channel::onMessage(std::function<void(message_variant data)> callback) { void Channel::onMessage(std::function<void(message_variant data)> callback) {
impl()->messageCallback = callback; impl()->messageCallback = callback;
impl()->flushPendingMessages();
// Pass pending messages
while (auto message = receive())
impl()->messageCallback(*message);
} }
void Channel::onMessage(std::function<void(binary data)> binaryCallback, void Channel::onMessage(std::function<void(binary data)> binaryCallback,

View File

@ -17,11 +17,11 @@
*/ */
#include "datachannel.hpp" #include "datachannel.hpp"
#include "globals.hpp"
#include "common.hpp" #include "common.hpp"
#include "peerconnection.hpp" #include "peerconnection.hpp"
#include "impl/datachannel.hpp" #include "impl/datachannel.hpp"
#include "impl/internals.hpp"
#include "impl/peerconnection.hpp" #include "impl/peerconnection.hpp"
#ifdef _WIN32 #ifdef _WIN32

View File

@ -19,6 +19,8 @@
#include "description.hpp" #include "description.hpp"
#include "impl/internals.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cctype> #include <cctype>

95
src/global.cpp Normal file
View File

@ -0,0 +1,95 @@
/**
* Copyright (c) 2020-2021 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "plog/Appenders/ColorConsoleAppender.h"
#include "plog/Formatters/FuncMessageFormatter.h"
#include "plog/Formatters/TxtFormatter.h"
#include "plog/Init.h"
#include "plog/Log.h"
#include "plog/Logger.h"
//
#include "global.hpp"
#include "impl/init.hpp"
#include <mutex>
#ifdef _WIN32
#include <codecvt>
#include <locale>
#endif
namespace rtc {
struct LogAppender : public plog::IAppender {
synchronized_callback<LogLevel, string> callback;
void write(const plog::Record &record) override {
const auto severity = record.getSeverity();
auto formatted = plog::FuncMessageFormatter::format(record);
formatted.pop_back(); // remove newline
#ifdef _WIN32
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
std::string str = converter.to_bytes(formatted);
#else
std::string str = formatted;
#endif
if (!callback(static_cast<LogLevel>(severity), str))
std::cout << plog::severityToString(severity) << " " << str << std::endl;
}
};
void InitLogger(LogLevel level, LogCallback callback) {
static unique_ptr<LogAppender> appender;
const auto severity = static_cast<plog::Severity>(level);
if (appender) {
appender->callback = std::move(callback);
InitLogger(severity, nullptr); // change the severity
} else if (callback) {
appender = std::make_unique<LogAppender>();
appender->callback = std::move(callback);
InitLogger(severity, appender.get());
} else {
InitLogger(severity, nullptr); // log to cout
}
}
void InitLogger(plog::Severity severity, plog::IAppender *appender) {
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
static plog::Logger<0> *logger = nullptr;
static std::mutex mutex;
std::lock_guard lock(mutex);
if (!logger) {
logger = &plog::init(severity, appender ? appender : &consoleAppender);
PLOG_DEBUG << "Logger initialized";
} else {
logger->setMaxSeverity(severity);
if (appender)
logger->addAppender(appender);
}
}
void Preload() { Init::Preload(); }
void Cleanup() { Init::Cleanup(); }
void SetSctpSettings(SctpSettings s) { Init::SetSctpSettings(std::move(s)); }
} // namespace rtc

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel client example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA

View File

@ -1,25 +1,35 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "h264rtppacketizer.hpp" #include "h264rtppacketizer.hpp"
#include "impl/internals.hpp"
#include <cassert>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
namespace rtc { namespace rtc {
typedef enum { typedef enum {

View File

@ -20,22 +20,21 @@
namespace rtc::impl { namespace rtc::impl {
void Channel::triggerOpen() { openCallback(); } void Channel::triggerOpen() {
mOpenTriggered = true;
openCallback();
flushPendingMessages();
}
void Channel::triggerClosed() { closedCallback(); } void Channel::triggerClosed() { closedCallback(); }
void Channel::triggerError(string error) { errorCallback(error); } void Channel::triggerError(string error) { errorCallback(std::move(error)); }
void Channel::triggerAvailable(size_t count) { void Channel::triggerAvailable(size_t count) {
if (count == 1) if (count == 1)
availableCallback(); availableCallback();
while (messageCallback && count--) { flushPendingMessages();
auto message = receive();
if (!message)
break;
messageCallback(*message);
}
} }
void Channel::triggerBufferedAmount(size_t amount) { void Channel::triggerBufferedAmount(size_t amount) {
@ -45,13 +44,32 @@ void Channel::triggerBufferedAmount(size_t amount) {
bufferedAmountLowCallback(); bufferedAmountLowCallback();
} }
void Channel::flushPendingMessages() {
if (!mOpenTriggered)
return;
while (messageCallback) {
auto next = receive();
if (!next)
break;
messageCallback(*next);
}
}
void Channel::resetOpenCallback() {
mOpenTriggered = false;
openCallback = nullptr;
}
void Channel::resetCallbacks() { void Channel::resetCallbacks() {
mOpenTriggered = false;
openCallback = nullptr; openCallback = nullptr;
closedCallback = nullptr; closedCallback = nullptr;
errorCallback = nullptr; errorCallback = nullptr;
messageCallback = nullptr;
availableCallback = nullptr; availableCallback = nullptr;
bufferedAmountLowCallback = nullptr; bufferedAmountLowCallback = nullptr;
messageCallback = nullptr;
} }
} // namespace rtc::impl } // namespace rtc::impl

View File

@ -38,17 +38,23 @@ struct Channel {
virtual void triggerAvailable(size_t count); virtual void triggerAvailable(size_t count);
virtual void triggerBufferedAmount(size_t amount); virtual void triggerBufferedAmount(size_t amount);
virtual void resetCallbacks(); void flushPendingMessages();
void resetOpenCallback();
void resetCallbacks();
synchronized_stored_callback<> openCallback;
synchronized_stored_callback<> closedCallback;
synchronized_stored_callback<string> errorCallback;
synchronized_stored_callback<> availableCallback;
synchronized_stored_callback<> bufferedAmountLowCallback;
synchronized_callback<> openCallback;
synchronized_callback<> closedCallback;
synchronized_callback<string> errorCallback;
synchronized_callback<message_variant> messageCallback; synchronized_callback<message_variant> messageCallback;
synchronized_callback<> availableCallback;
synchronized_callback<> bufferedAmountLowCallback;
std::atomic<size_t> bufferedAmount = 0; std::atomic<size_t> bufferedAmount = 0;
std::atomic<size_t> bufferedAmountLowThreshold = 0; std::atomic<size_t> bufferedAmountLowThreshold = 0;
private:
std::atomic<bool> mOpenTriggered = false;
}; };
} // namespace rtc::impl } // namespace rtc::impl

View File

@ -18,7 +18,7 @@
#include "datachannel.hpp" #include "datachannel.hpp"
#include "common.hpp" #include "common.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "logcounter.hpp" #include "logcounter.hpp"
#include "peerconnection.hpp" #include "peerconnection.hpp"
#include "sctptransport.hpp" #include "sctptransport.hpp"
@ -170,6 +170,7 @@ size_t DataChannel::maxMessageSize() const {
} }
void DataChannel::shiftStream() { void DataChannel::shiftStream() {
std::shared_lock lock(mMutex);
if (mStream % 2 == 1) if (mStream % 2 == 1)
mStream -= 1; mStream -= 1;
} }

View File

@ -36,7 +36,7 @@ struct PeerConnection;
struct DataChannel : Channel, std::enable_shared_from_this<DataChannel> { struct DataChannel : Channel, std::enable_shared_from_this<DataChannel> {
DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol, DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
Reliability reliability); Reliability reliability);
~DataChannel(); virtual ~DataChannel();
void close(); void close();
void remoteClose(); void remoteClose();

View File

@ -17,7 +17,7 @@
*/ */
#include "dtlstransport.hpp" #include "dtlstransport.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "icetransport.hpp" #include "icetransport.hpp"
#include <chrono> #include <chrono>

View File

@ -18,7 +18,7 @@
#include "icetransport.hpp" #include "icetransport.hpp"
#include "configuration.hpp" #include "configuration.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "transport.hpp" #include "transport.hpp"
#include <iostream> #include <iostream>

View File

@ -17,7 +17,7 @@
*/ */
#include "init.hpp" #include "init.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "impl/certificate.hpp" #include "impl/certificate.hpp"
#include "impl/dtlstransport.hpp" #include "impl/dtlstransport.hpp"
@ -42,7 +42,6 @@ namespace rtc {
namespace { namespace {
void doInit() { void doInit() {
PLOG_DEBUG << "Global initialization";
#ifdef _WIN32 #ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;
@ -69,8 +68,6 @@ void doInit() {
} }
void doCleanup() { void doCleanup() {
PLOG_DEBUG << "Global cleanup";
impl::ThreadPool::Instance().join(); impl::ThreadPool::Instance().join();
impl::SctpTransport::Cleanup(); impl::SctpTransport::Cleanup();
@ -92,6 +89,7 @@ void doCleanup() {
weak_ptr<void> Init::Weak; weak_ptr<void> Init::Weak;
shared_ptr<void> *Init::Global = nullptr; shared_ptr<void> *Init::Global = nullptr;
bool Init::Initialized = false; bool Init::Initialized = false;
SctpSettings Init::CurrentSctpSettings = {};
std::recursive_mutex Init::Mutex; std::recursive_mutex Init::Mutex;
init_token Init::Token() { init_token Init::Token() {
@ -118,10 +116,20 @@ void Init::Cleanup() {
Global = nullptr; Global = nullptr;
} }
void Init::SetSctpSettings(SctpSettings s) {
auto token = Token();
std::unique_lock lock(Mutex);
impl::SctpTransport::SetSettings(s);
CurrentSctpSettings = std::move(s); // store for next init
}
Init::Init() { Init::Init() {
// Mutex is locked by Token() here // Mutex is locked by Token() here
if (!std::exchange(Initialized, true)) if (!std::exchange(Initialized, true)) {
PLOG_DEBUG << "Global initialization";
doInit(); doInit();
impl::SctpTransport::SetSettings(CurrentSctpSettings);
}
} }
Init::~Init() { Init::~Init() {
@ -130,8 +138,11 @@ Init::~Init() {
std::unique_lock lock(Mutex); std::unique_lock lock(Mutex);
if (Global) if (Global)
return; return;
if (std::exchange(Initialized, false))
if (std::exchange(Initialized, false)) {
PLOG_DEBUG << "Global cleanup";
doCleanup(); doCleanup();
}
}); });
t.detach(); t.detach();
} }

View File

@ -16,22 +16,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_INIT_H #ifndef RTC_IMPL_INIT_H
#define RTC_INIT_H #define RTC_IMPL_INIT_H
#include "common.hpp" #include "common.hpp"
#include "global.hpp" // for SctpSettings
#include <chrono>
#include <mutex> #include <mutex>
namespace rtc { namespace rtc {
using init_token = shared_ptr<void>; using init_token = shared_ptr<void>;
class RTC_CPP_EXPORT Init { class Init {
public: public:
static init_token Token(); static init_token Token();
static void Preload(); static void Preload();
static void Cleanup(); static void Cleanup();
static void SetSctpSettings(SctpSettings s);
~Init(); ~Init();
@ -41,12 +44,10 @@ private:
static weak_ptr<void> Weak; static weak_ptr<void> Weak;
static shared_ptr<void> *Global; static shared_ptr<void> *Global;
static bool Initialized; static bool Initialized;
static SctpSettings CurrentSctpSettings;
static std::recursive_mutex Mutex; static std::recursive_mutex Mutex;
}; };
inline void Preload() { Init::Preload(); }
inline void Cleanup() { Init::Cleanup(); }
} // namespace rtc } // namespace rtc
#endif #endif

View File

@ -16,11 +16,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RTC_GLOBALS_H #ifndef RTC_IMPL_INTERNALS_H
#define RTC_GLOBALS_H #define RTC_IMPL_INTERNALS_H
#include "common.hpp" #include "common.hpp"
// Disable warnings before including plog
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#elif defined(_MSC_VER)
#pragma warning(push, 0)
#endif
#include "plog/Log.h"
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
namespace rtc { namespace rtc {
const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length

View File

@ -21,7 +21,7 @@
#include "certificate.hpp" #include "certificate.hpp"
#include "common.hpp" #include "common.hpp"
#include "dtlstransport.hpp" #include "dtlstransport.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "icetransport.hpp" #include "icetransport.hpp"
#include "logcounter.hpp" #include "logcounter.hpp"
#include "peerconnection.hpp" #include "peerconnection.hpp"
@ -189,7 +189,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
auto certificate = mCertificate.get(); auto certificate = mCertificate.get();
auto lower = std::atomic_load(&mIceTransport); auto lower = std::atomic_load(&mIceTransport);
auto verifierCallback = weak_bind(&PeerConnection::checkFingerprint, this, _1); auto verifierCallback = weak_bind(&PeerConnection::checkFingerprint, this, _1);
auto stateChangeCallback = auto dtlsStateChangeCallback =
[this, weak_this = weak_from_this()](DtlsTransport::State transportState) { [this, weak_this = weak_from_this()](DtlsTransport::State transportState) {
auto shared_this = weak_this.lock(); auto shared_this = weak_this.lock();
if (!shared_this) if (!shared_this)
@ -224,7 +224,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
// DTLS-SRTP // DTLS-SRTP
transport = std::make_shared<DtlsSrtpTransport>( transport = std::make_shared<DtlsSrtpTransport>(
lower, certificate, config.mtu, verifierCallback, lower, certificate, config.mtu, verifierCallback,
weak_bind(&PeerConnection::forwardMedia, this, _1), stateChangeCallback); weak_bind(&PeerConnection::forwardMedia, this, _1), dtlsStateChangeCallback);
#else #else
PLOG_WARNING << "Ignoring media support (not compiled with media support)"; PLOG_WARNING << "Ignoring media support (not compiled with media support)";
#endif #endif
@ -233,7 +233,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
if (!transport) { if (!transport) {
// DTLS only // DTLS only
transport = std::make_shared<DtlsTransport>(lower, certificate, config.mtu, transport = std::make_shared<DtlsTransport>(lower, certificate, config.mtu,
verifierCallback, stateChangeCallback); verifierCallback, dtlsStateChangeCallback);
} }
std::atomic_store(&mDtlsTransport, transport); std::atomic_store(&mDtlsTransport, transport);
@ -262,6 +262,9 @@ shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
if (!remote || !remote->application()) if (!remote || !remote->application())
throw std::logic_error("Starting SCTP transport without application description"); throw std::logic_error("Starting SCTP transport without application description");
// This is the last occasion to ensure the stream numbers are coherent with the role
shiftDataChannels();
uint16_t sctpPort = remote->application()->sctpPort().value_or(DEFAULT_SCTP_PORT); uint16_t sctpPort = remote->application()->sctpPort().value_or(DEFAULT_SCTP_PORT);
auto lower = std::atomic_load(&mDtlsTransport); auto lower = std::atomic_load(&mDtlsTransport);
auto transport = std::make_shared<SctpTransport>( auto transport = std::make_shared<SctpTransport>(
@ -437,8 +440,8 @@ void PeerConnection::forwardMedia(message_ptr message) {
offset += header->lengthInBytes(); offset += header->lengthInBytes();
if (header->payloadType() == 205 || header->payloadType() == 206) { if (header->payloadType() == 205 || header->payloadType() == 206) {
auto rtcpfb = reinterpret_cast<RTCP_FB_HEADER *>(header); auto rtcpfb = reinterpret_cast<RTCP_FB_HEADER *>(header);
ssrcs.insert(rtcpfb->getPacketSenderSSRC()); ssrcs.insert(rtcpfb->packetSenderSSRC());
ssrcs.insert(rtcpfb->getMediaSourceSSRC()); ssrcs.insert(rtcpfb->mediaSourceSSRC());
} else if (header->payloadType() == 200 || header->payloadType() == 201) { } else if (header->payloadType() == 200 || header->payloadType() == 201) {
auto rtcpsr = reinterpret_cast<RTCP_SR *>(header); auto rtcpsr = reinterpret_cast<RTCP_SR *>(header);
@ -549,8 +552,7 @@ void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
channel->triggerBufferedAmount(amount); channel->triggerBufferedAmount(amount);
} }
shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role, string label, shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(string label, DataChannelInit init) {
DataChannelInit init) {
std::unique_lock lock(mDataChannelsMutex); // we are going to emplace std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
uint16_t stream; uint16_t stream;
if (init.id) { if (init.id) {
@ -558,6 +560,13 @@ shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role rol
if (stream == 65535) if (stream == 65535)
throw std::invalid_argument("Invalid DataChannel id"); throw std::invalid_argument("Invalid DataChannel id");
} else { } else {
// RFC 5763: The answerer MUST use either a setup attribute value of setup:active or
// setup:passive. [...] Thus, setup:active is RECOMMENDED.
// See https://tools.ietf.org/html/rfc5763#section-5
// Therefore, we assume passive role if we are the offerer.
auto iceTransport = getIceTransport();
auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
// RFC 8832: The peer that initiates opening a data channel selects a stream identifier for // RFC 8832: The peer that initiates opening a data channel selects a stream identifier for
// which the corresponding incoming and outgoing streams are unused. If the side is acting // which the corresponding incoming and outgoing streams are unused. If the side is acting
// as the DTLS client, it MUST choose an even stream identifier; if the side is acting as // as the DTLS client, it MUST choose an even stream identifier; if the side is acting as
@ -729,7 +738,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
} }
void PeerConnection::processLocalDescription(Description description) { void PeerConnection::processLocalDescription(Description description) {
const size_t localSctpPort = DEFAULT_SCTP_PORT; const uint16_t localSctpPort = DEFAULT_SCTP_PORT;
const size_t localMaxMessageSize = const size_t localMaxMessageSize =
config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE); config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
@ -907,6 +916,10 @@ void PeerConnection::processRemoteDescription(Description description) {
auto iceTransport = initIceTransport(); auto iceTransport = initIceTransport();
iceTransport->setRemoteDescription(std::move(description)); iceTransport->setRemoteDescription(std::move(description));
// Since we assumed passive role during DataChannel creation, we might need to shift the stream
// numbers from odd to even.
shiftDataChannels();
if (description.hasApplication()) { if (description.hasApplication()) {
auto dtlsTransport = std::atomic_load(&mDtlsTransport); auto dtlsTransport = std::atomic_load(&mDtlsTransport);
auto sctpTransport = std::atomic_load(&mSctpTransport); auto sctpTransport = std::atomic_load(&mSctpTransport);
@ -960,33 +973,54 @@ string PeerConnection::localBundleMid() const {
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) { void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
auto dataChannel = weakDataChannel.lock(); auto dataChannel = weakDataChannel.lock();
if (dataChannel) if (dataChannel) {
dataChannel->resetOpenCallback(); // might be set internally
mPendingDataChannels.push(std::move(dataChannel)); mPendingDataChannels.push(std::move(dataChannel));
}
triggerPendingDataChannels();
}
void PeerConnection::triggerTrack(weak_ptr<Track> weakTrack) {
auto track = weakTrack.lock();
if (track) {
track->resetOpenCallback(); // might be set internally
mPendingTracks.push(std::move(track));
}
triggerPendingTracks();
}
void PeerConnection::triggerPendingDataChannels() {
while (dataChannelCallback) { while (dataChannelCallback) {
auto next = mPendingDataChannels.tryPop(); auto next = mPendingDataChannels.tryPop();
if (!next) if (!next)
break; break;
mProcessor->enqueue(dataChannelCallback.wrap(), auto impl = std::move(*next);
std::make_shared<rtc::DataChannel>(std::move(*next))); dataChannelCallback(std::make_shared<rtc::DataChannel>(impl));
impl->triggerOpen();
} }
} }
void PeerConnection::triggerTrack(weak_ptr<Track> weakTrack) { void PeerConnection::triggerPendingTracks() {
auto track = weakTrack.lock();
if (track)
mPendingTracks.push(std::move(track));
while (trackCallback) { while (trackCallback) {
auto next = mPendingTracks.tryPop(); auto next = mPendingTracks.tryPop();
if (!next) if (!next)
break; break;
mProcessor->enqueue(trackCallback.wrap(), std::make_shared<rtc::Track>(std::move(*next))); auto impl = std::move(*next);
trackCallback(std::make_shared<rtc::Track>(impl));
impl->triggerOpen();
} }
} }
void PeerConnection::flushPendingDataChannels() {
mProcessor->enqueue(std::bind(&PeerConnection::triggerPendingDataChannels, this));
}
void PeerConnection::flushPendingTracks() {
mProcessor->enqueue(std::bind(&PeerConnection::triggerPendingTracks, this));
}
bool PeerConnection::changeState(State newState) { bool PeerConnection::changeState(State newState) {
State current; State current;
do { do {

View File

@ -65,8 +65,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
void forwardBufferedAmount(uint16_t stream, size_t amount); void forwardBufferedAmount(uint16_t stream, size_t amount);
optional<string> getMidFromSsrc(uint32_t ssrc); optional<string> getMidFromSsrc(uint32_t ssrc);
shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label, shared_ptr<DataChannel> emplaceDataChannel(string label, DataChannelInit init);
DataChannelInit init);
shared_ptr<DataChannel> findDataChannel(uint16_t stream); shared_ptr<DataChannel> findDataChannel(uint16_t stream);
void shiftDataChannels(); void shiftDataChannels();
void iterateDataChannels(std::function<void(shared_ptr<DataChannel> channel)> func); void iterateDataChannels(std::function<void(shared_ptr<DataChannel> channel)> func);
@ -85,8 +84,14 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
void processRemoteCandidate(Candidate candidate); void processRemoteCandidate(Candidate candidate);
string localBundleMid() const; string localBundleMid() const;
void triggerDataChannel(weak_ptr<DataChannel> weakDataChannel = {}); void triggerDataChannel(weak_ptr<DataChannel> weakDataChannel);
void triggerTrack(weak_ptr<Track> weakTrack = {}); void triggerTrack(weak_ptr<Track> weakTrack);
void triggerPendingDataChannels();
void triggerPendingTracks();
void flushPendingDataChannels();
void flushPendingTracks();
bool changeState(State newState); bool changeState(State newState);
bool changeGatheringState(GatheringState newState); bool changeGatheringState(GatheringState newState);

View File

@ -18,12 +18,13 @@
#include "sctptransport.hpp" #include "sctptransport.hpp"
#include "dtlstransport.hpp" #include "dtlstransport.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "logcounter.hpp" #include "logcounter.hpp"
#include <chrono> #include <chrono>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <limits>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -54,6 +55,26 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace std::chrono; using namespace std::chrono;
namespace {
template <typename T> uint16_t to_uint16(T i) {
if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
std::numeric_limits<uint16_t>::max())
return static_cast<uint16_t>(i);
else
throw std::invalid_argument("Integer out of range");
}
template <typename T> uint32_t to_uint32(T i) {
if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
std::numeric_limits<uint32_t>::max())
return static_cast<uint32_t>(i);
else
throw std::invalid_argument("Integer out of range");
}
} // namespace
namespace rtc::impl { namespace rtc::impl {
static LogCounter COUNTER_UNKNOWN_PPID(plog::warning, static LogCounter COUNTER_UNKNOWN_PPID(plog::warning,
@ -69,7 +90,8 @@ std::shared_mutex SctpTransport::InstancesMutex;
void SctpTransport::Init() { void SctpTransport::Init() {
usrsctp_init(0, &SctpTransport::WriteCallback, nullptr); usrsctp_init(0, &SctpTransport::WriteCallback, nullptr);
usrsctp_sysctl_set_sctp_ecn_enable(0); usrsctp_sysctl_set_sctp_pr_enable(1); // Enable Partial Reliability Extension (RFC 3758)
usrsctp_sysctl_set_sctp_ecn_enable(0); // Disable Explicit Congestion Notification
usrsctp_sysctl_set_sctp_init_rtx_max_default(5); usrsctp_sysctl_set_sctp_init_rtx_max_default(5);
usrsctp_sysctl_set_sctp_path_rtx_max_default(5); usrsctp_sysctl_set_sctp_path_rtx_max_default(5);
usrsctp_sysctl_set_sctp_assoc_rtx_max_default(5); // single path usrsctp_sysctl_set_sctp_assoc_rtx_max_default(5); // single path
@ -78,21 +100,31 @@ void SctpTransport::Init() {
usrsctp_sysctl_set_sctp_rto_initial_default(1 * 1000); // ms usrsctp_sysctl_set_sctp_rto_initial_default(1 * 1000); // ms
usrsctp_sysctl_set_sctp_init_rto_max_default(10 * 1000); // ms usrsctp_sysctl_set_sctp_init_rto_max_default(10 * 1000); // ms
usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms
}
usrsctp_sysctl_set_sctp_max_chunks_on_queue(10 * 1024); void SctpTransport::SetSettings(const SctpSettings &s) {
// The send and receive window size of usrsctp is 256KiB, which is too small for realistic RTTs,
// therefore we increase it to 1MiB by default for better performance.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1051685
usrsctp_sysctl_set_sctp_recvspace(to_uint32(s.recvBufferSize.value_or(1024 * 1024)));
usrsctp_sysctl_set_sctp_sendspace(to_uint32(s.sendBufferSize.value_or(1024 * 1024)));
// Use default congestion control (RFC 4960) // Increase maximum chunks number on queue to 10K by default
usrsctp_sysctl_set_sctp_max_chunks_on_queue(to_uint32(s.maxChunksOnQueue.value_or(10 * 1024)));
// Increase initial congestion window size to 10 MTUs (RFC 6928) by default
usrsctp_sysctl_set_sctp_initial_cwnd(to_uint32(s.initialCongestionWindow.value_or(10)));
// Set max burst to 10 MTUs by default (max burst is initially 0, meaning disabled)
usrsctp_sysctl_set_sctp_max_burst_default(to_uint32(s.maxBurst.value_or(10)));
// Use standard SCTP congestion control (RFC 4960) by default
// See https://github.com/paullouisageneau/libdatachannel/issues/354 // See https://github.com/paullouisageneau/libdatachannel/issues/354
usrsctp_sysctl_set_sctp_default_cc_module(0); usrsctp_sysctl_set_sctp_default_cc_module(to_uint32(s.congestionControlModule.value_or(0)));
// Enable Partial Reliability Extension (RFC 3758) // Reduce SACK delay to 20ms by default
usrsctp_sysctl_set_sctp_pr_enable(1); usrsctp_sysctl_set_sctp_delayed_sack_time_default(
to_uint32(s.delayedSackTime.value_or(20ms).count()));
// Increase the initial window size to 10 MTUs (RFC 6928)
usrsctp_sysctl_set_sctp_initial_cwnd(10);
// Reduce SACK delay from the default 200ms to 20ms
usrsctp_sysctl_set_sctp_delayed_sack_time_default(20); // ms
} }
void SctpTransport::Cleanup() { void SctpTransport::Cleanup() {
@ -195,7 +227,7 @@ SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &c
// The MTU value provided specifies the space available for chunks in the // The MTU value provided specifies the space available for chunks in the
// packet, so we also subtract the SCTP header size. // packet, so we also subtract the SCTP header size.
size_t pmtu = config.mtu.value_or(DEFAULT_MTU) - 12 - 37 - 8 - 40; // SCTP/DTLS/UDP/IPv6 size_t pmtu = config.mtu.value_or(DEFAULT_MTU) - 12 - 37 - 8 - 40; // SCTP/DTLS/UDP/IPv6
spp.spp_pathmtu = uint32_t(pmtu); spp.spp_pathmtu = to_uint32(pmtu);
PLOG_VERBOSE << "Path MTU discovery disabled, SCTP MTU set to " << pmtu; PLOG_VERBOSE << "Path MTU discovery disabled, SCTP MTU set to " << pmtu;
} }
@ -222,18 +254,28 @@ SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &c
throw std::runtime_error("Could not disable SCTP fragmented interleave, errno=" + throw std::runtime_error("Could not disable SCTP fragmented interleave, errno=" +
std::to_string(errno)); std::to_string(errno));
// The default send and receive window size of usrsctp is 256KiB, which is too small for int rcvBuf = 0;
// realistic RTTs, therefore we increase it to at least 1MiB for better performance. socklen_t rcvBufLen = sizeof(rcvBuf);
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1051685 if (usrsctp_getsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &rcvBuf, &rcvBufLen))
const size_t minBufferSize = 1024 * 1024; throw std::runtime_error("Could not get SCTP recv buffer size, errno=" +
std::to_string(errno));
int sndBuf = 0;
socklen_t sndBufLen = sizeof(sndBuf);
if (usrsctp_getsockopt(mSock, SOL_SOCKET, SO_SNDBUF, &sndBuf, &sndBufLen))
throw std::runtime_error("Could not get SCTP send buffer size, errno=" +
std::to_string(errno));
// Ensure the buffer is also large enough to accomodate the largest messages // Ensure the buffer is also large enough to accomodate the largest messages
const size_t maxMessageSize = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE); const size_t maxMessageSize = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
const int bufferSize = int(std::max(minBufferSize, maxMessageSize * 2)); // usrsctp reads as int const int minBuf = int(std::min(maxMessageSize, size_t(std::numeric_limits<int>::max())));
if (usrsctp_setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize))) rcvBuf = std::max(rcvBuf, minBuf);
sndBuf = std::max(sndBuf, minBuf);
if (usrsctp_setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &rcvBuf, sizeof(rcvBuf)))
throw std::runtime_error("Could not set SCTP recv buffer size, errno=" + throw std::runtime_error("Could not set SCTP recv buffer size, errno=" +
std::to_string(errno)); std::to_string(errno));
if (usrsctp_setsockopt(mSock, SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)))
if (usrsctp_setsockopt(mSock, SOL_SOCKET, SO_SNDBUF, &sndBuf, sizeof(sndBuf)))
throw std::runtime_error("Could not set SCTP send buffer size, errno=" + throw std::runtime_error("Could not set SCTP send buffer size, errno=" +
std::to_string(errno)); std::to_string(errno));
} }
@ -336,7 +378,7 @@ bool SctpTransport::send(message_ptr message) {
return true; return true;
mSendQueue.push(message); mSendQueue.push(message);
updateBufferedAmount(uint16_t(message->stream), long(message_size_func(message))); updateBufferedAmount(to_uint16(message->stream), ptrdiff_t(message_size_func(message)));
return false; return false;
} }
@ -353,7 +395,7 @@ bool SctpTransport::flush() {
} }
void SctpTransport::closeStream(unsigned int stream) { void SctpTransport::closeStream(unsigned int stream) {
send(make_message(0, Message::Reset, uint16_t(stream))); send(make_message(0, Message::Reset, to_uint16(stream)));
} }
void SctpTransport::incoming(message_ptr message) { void SctpTransport::incoming(message_ptr message) {
@ -458,7 +500,7 @@ bool SctpTransport::trySendQueue() {
if (!trySendMessage(message)) if (!trySendMessage(message))
return false; return false;
mSendQueue.pop(); mSendQueue.pop();
updateBufferedAmount(uint16_t(message->stream), -long(message_size_func(message))); updateBufferedAmount(to_uint16(message->stream), -ptrdiff_t(message_size_func(message)));
} }
return true; return true;
} }
@ -511,12 +553,12 @@ bool SctpTransport::trySendMessage(message_ptr message) {
case Reliability::Type::Rexmit: case Reliability::Type::Rexmit:
spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
spa.sendv_prinfo.pr_value = uint32_t(std::get<int>(reliability.rexmit)); spa.sendv_prinfo.pr_value = to_uint32(std::get<int>(reliability.rexmit));
break; break;
case Reliability::Type::Timed: case Reliability::Type::Timed:
spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
spa.sendv_prinfo.pr_value = uint32_t(std::get<milliseconds>(reliability.rexmit).count()); spa.sendv_prinfo.pr_value = to_uint32(std::get<milliseconds>(reliability.rexmit).count());
break; break;
default: default:
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_NONE; spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_NONE;
@ -548,10 +590,10 @@ bool SctpTransport::trySendMessage(message_ptr message) {
return true; return true;
} }
void SctpTransport::updateBufferedAmount(uint16_t streamId, long delta) { void SctpTransport::updateBufferedAmount(uint16_t streamId, ptrdiff_t delta) {
// Requires mSendMutex to be locked // Requires mSendMutex to be locked
auto it = mBufferedAmount.insert(std::make_pair(streamId, 0)).first; auto it = mBufferedAmount.insert(std::make_pair(streamId, 0)).first;
size_t amount = size_t(std::max(long(it->second) + delta, long(0))); size_t amount = size_t(std::max(ptrdiff_t(it->second) + delta, ptrdiff_t(0)));
if (amount == 0) if (amount == 0)
mBufferedAmount.erase(it); mBufferedAmount.erase(it);
else else

View File

@ -39,6 +39,7 @@ namespace rtc::impl {
class SctpTransport final : public Transport { class SctpTransport final : public Transport {
public: public:
static void Init(); static void Init();
static void SetSettings(const SctpSettings &s);
static void Cleanup(); static void Cleanup();
using amount_callback = std::function<void(uint16_t streamId, size_t amount)>; using amount_callback = std::function<void(uint16_t streamId, size_t amount)>;
@ -83,7 +84,7 @@ private:
void doFlush(); void doFlush();
bool trySendQueue(); bool trySendQueue();
bool trySendMessage(message_ptr message); bool trySendMessage(message_ptr message);
void updateBufferedAmount(uint16_t streamId, long delta); void updateBufferedAmount(uint16_t streamId, ptrdiff_t delta);
void triggerBufferedAmount(uint16_t streamId, size_t amount); void triggerBufferedAmount(uint16_t streamId, size_t amount);
void sendReset(uint16_t streamId); void sendReset(uint16_t streamId);

View File

@ -17,7 +17,7 @@
*/ */
#include "tcptransport.hpp" #include "tcptransport.hpp"
#include "globals.hpp" #include "internals.hpp"
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET

View File

@ -21,6 +21,7 @@
#include "common.hpp" #include "common.hpp"
#include "init.hpp" #include "init.hpp"
#include "internals.hpp"
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>

View File

@ -18,6 +18,8 @@
#include "tls.hpp" #include "tls.hpp"
#include "internals.hpp"
#if USE_GNUTLS #if USE_GNUTLS
namespace rtc::gnutls { namespace rtc::gnutls {

View File

@ -17,7 +17,7 @@
*/ */
#include "track.hpp" #include "track.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "logcounter.hpp" #include "logcounter.hpp"
#include "peerconnection.hpp" #include "peerconnection.hpp"

View File

@ -20,6 +20,7 @@
#define RTC_IMPL_TRANSPORT_H #define RTC_IMPL_TRANSPORT_H
#include "common.hpp" #include "common.hpp"
#include "internals.hpp"
#include "message.hpp" #include "message.hpp"
#include <atomic> #include <atomic>

View File

@ -19,7 +19,7 @@
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
#include "websocket.hpp" #include "websocket.hpp"
#include "globals.hpp" #include "internals.hpp"
#include "common.hpp" #include "common.hpp"
#include "threadpool.hpp" #include "threadpool.hpp"

View File

@ -39,7 +39,7 @@
#ifndef htonll #ifndef htonll
#define htonll(x) \ #define htonll(x) \
((uint64_t)htonl(((uint64_t)(x)&0xFFFFFFFF) << 32) | (uint64_t)htonl((uint64_t)(x) >> 32)) ((uint64_t)(((uint64_t)htonl((uint32_t)(x))) << 32) | (uint64_t)htonl((uint32_t)((x) >> 32)))
#endif #endif
#ifndef ntohll #ifndef ntohll
#define ntohll(x) htonll(x) #define ntohll(x) htonll(x)

View File

@ -1,47 +0,0 @@
/**
* 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 "log.hpp"
#include "plog/Appenders/ColorConsoleAppender.h"
#include "plog/Formatters/TxtFormatter.h"
#include "plog/Init.h"
#include "plog/Log.h"
#include "plog/Logger.h"
#include <mutex>
namespace rtc {
void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
void InitLogger(plog::Severity severity, plog::IAppender *appender) {
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
static plog::Logger<0> *logger = nullptr;
static std::mutex mutex;
std::lock_guard lock(mutex);
if (!logger) {
logger = &plog::init(severity, appender ? appender : &consoleAppender);
PLOG_DEBUG << "Logger initialized";
} else {
logger->setMaxSeverity(severity);
if (appender)
logger->addAppender(appender);
}
}
} // namespace rtc

View File

@ -1,24 +1,29 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "mediachainablehandler.hpp" #include "mediachainablehandler.hpp"
#include "impl/internals.hpp"
#include <cassert>
namespace rtc { namespace rtc {
MediaChainableHandler::MediaChainableHandler(shared_ptr<MediaHandlerRootElement> root): MediaHandler(), root(root), leaf(root) { } MediaChainableHandler::MediaChainableHandler(shared_ptr<MediaHandlerRootElement> root): MediaHandler(), root(root), leaf(root) { }

View File

@ -1,24 +1,29 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "mediahandlerelement.hpp" #include "mediahandlerelement.hpp"
#include "impl/internals.hpp"
#include <cassert>
namespace rtc { namespace rtc {
ChainedMessagesProduct make_chained_messages_product() { ChainedMessagesProduct make_chained_messages_product() {

View File

@ -1,18 +1,19 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA

View File

@ -1,25 +1,26 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "nalunit.hpp" #include "nalunit.hpp"
#include "globals.hpp"
#include "impl/internals.hpp"
#include <cmath> #include <cmath>

View File

@ -1,19 +1,19 @@
/* /**
* libdatachannel client example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA

View File

@ -1,25 +1,27 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "opusrtppacketizer.hpp" #include "opusrtppacketizer.hpp"
#include <cassert>
namespace rtc { namespace rtc {
OpusRtpPacketizer::OpusRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig) OpusRtpPacketizer::OpusRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)

View File

@ -24,6 +24,7 @@
#include "impl/certificate.hpp" #include "impl/certificate.hpp"
#include "impl/dtlstransport.hpp" #include "impl/dtlstransport.hpp"
#include "impl/icetransport.hpp" #include "impl/icetransport.hpp"
#include "impl/internals.hpp"
#include "impl/peerconnection.hpp" #include "impl/peerconnection.hpp"
#include "impl/processor.hpp" #include "impl/processor.hpp"
#include "impl/sctptransport.hpp" #include "impl/sctptransport.hpp"
@ -223,11 +224,6 @@ void PeerConnection::setRemoteDescription(Description description) {
// This is an offer, we need to answer // This is an offer, we need to answer
if (!impl()->config.disableAutoNegotiation) if (!impl()->config.disableAutoNegotiation)
setLocalDescription(Description::Type::Answer); setLocalDescription(Description::Type::Answer);
} else {
// This is an answer
// Since we assumed passive role during DataChannel creation, we need to shift the
// stream numbers by one to shift them from odd to even.
impl()->shiftDataChannels();
} }
for (const auto &candidate : remoteCandidates) for (const auto &candidate : remoteCandidates)
@ -250,14 +246,7 @@ optional<string> PeerConnection::remoteAddress() const {
} }
shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, DataChannelInit init) { shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, DataChannelInit init) {
// RFC 5763: The answerer MUST use either a setup attribute value of setup:active or auto channelImpl = impl()->emplaceDataChannel(std::move(label), std::move(init));
// setup:passive. [...] Thus, setup:active is RECOMMENDED.
// See https://tools.ietf.org/html/rfc5763#section-5
// Therefore, we assume passive role when we are the offerer.
auto iceTransport = impl()->getIceTransport();
auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
auto channelImpl = impl()->emplaceDataChannel(role, std::move(label), std::move(init));
auto channel = std::make_shared<DataChannel>(channelImpl); auto channel = std::make_shared<DataChannel>(channelImpl);
if (auto transport = impl()->getSctpTransport()) if (auto transport = impl()->getSctpTransport())
@ -278,7 +267,7 @@ shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, DataChan
void PeerConnection::onDataChannel( void PeerConnection::onDataChannel(
std::function<void(shared_ptr<DataChannel> dataChannel)> callback) { std::function<void(shared_ptr<DataChannel> dataChannel)> callback) {
impl()->dataChannelCallback = callback; impl()->dataChannelCallback = callback;
impl()->triggerDataChannel(); // trigger pending DataChannels impl()->flushPendingDataChannels();
} }
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) { std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
@ -293,7 +282,7 @@ std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description)
void PeerConnection::onTrack(std::function<void(std::shared_ptr<Track>)> callback) { void PeerConnection::onTrack(std::function<void(std::shared_ptr<Track>)> callback) {
impl()->trackCallback = callback; impl()->trackCallback = callback;
impl()->triggerTrack(); // trigger pending tracks impl()->flushPendingTracks();
} }
void PeerConnection::onLocalDescription(std::function<void(Description description)> callback) { void PeerConnection::onLocalDescription(std::function<void(Description description)> callback) {

View File

@ -1,24 +1,29 @@
/** /**
* libdatachannel streamer example * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "rtcpnackresponder.hpp" #include "rtcpnackresponder.hpp"
#include "impl/internals.hpp"
#include <cassert>
namespace rtc { namespace rtc {
RtcpNackResponder::Storage::Element::Element(binary_ptr packet, uint16_t sequenceNumber, shared_ptr<Element> next) RtcpNackResponder::Storage::Element::Element(binary_ptr packet, uint16_t sequenceNumber, shared_ptr<Element> next)

View File

@ -97,7 +97,7 @@ void RtcpReceivingSession::requestBitrate(unsigned int newBitrate) {
} }
void RtcpReceivingSession::pushREMB(unsigned int bitrate) { void RtcpReceivingSession::pushREMB(unsigned int bitrate) {
message_ptr msg = make_message(RTCP_REMB::sizeWithSSRCs(1), Message::Type::Control); message_ptr msg = make_message(RTCP_REMB::SizeWithSSRCs(1), Message::Type::Control);
auto remb = reinterpret_cast<RTCP_REMB *>(msg->data()); auto remb = reinterpret_cast<RTCP_REMB *>(msg->data());
remb->preparePacket(mSsrc, 1, bitrate); remb->preparePacket(mSsrc, 1, bitrate);
remb->setSsrc(0, mSsrc); remb->setSsrc(0, mSsrc);
@ -106,7 +106,7 @@ void RtcpReceivingSession::pushREMB(unsigned int bitrate) {
} }
void RtcpReceivingSession::pushRR(unsigned int lastSR_delay) { void RtcpReceivingSession::pushRR(unsigned int lastSR_delay) {
auto msg = make_message(RTCP_RR::sizeWithReportBlocks(1), Message::Type::Control); auto msg = make_message(RTCP_RR::SizeWithReportBlocks(1), Message::Type::Control);
auto rr = reinterpret_cast<RTCP_RR *>(msg->data()); auto rr = reinterpret_cast<RTCP_RR *>(msg->data());
rr->preparePacket(mSsrc, 1); rr->preparePacket(mSsrc, 1);
rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, uint16_t(mGreatestSeqNo), 0, 0, mSyncNTPTS, rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, uint16_t(mGreatestSeqNo), 0, 0, mSyncNTPTS,
@ -132,7 +132,7 @@ bool RtcpReceivingSession::requestKeyframe() {
} }
void RtcpReceivingSession::pushPLI() { void RtcpReceivingSession::pushPLI() {
auto msg = make_message(RTCP_PLI::size(), Message::Type::Control); auto msg = make_message(RTCP_PLI::Size(), Message::Type::Control);
auto *pli = reinterpret_cast<RTCP_PLI *>(msg->data()); auto *pli = reinterpret_cast<RTCP_PLI *>(msg->data());
pli->preparePacket(mSsrc); pli->preparePacket(mSsrc);
send(msg); send(msg);

View File

@ -1,24 +1,28 @@
/** /**
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "rtcpsrreporter.hpp" #include "rtcpsrreporter.hpp"
#include <cmath>
#include <cassert>
namespace rtc { namespace rtc {
ChainedOutgoingProduct RtcpSrReporter::processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) { ChainedOutgoingProduct RtcpSrReporter::processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) {
@ -60,8 +64,8 @@ uint64_t RtcpSrReporter::secondsToNTP(double seconds) {
void RtcpSrReporter::setNeedsToReport() { needsToReport = true; } void RtcpSrReporter::setNeedsToReport() { needsToReport = true; }
message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) { message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) {
auto srSize = RTCP_SR::size(0); auto srSize = RTCP_SR::Size(0);
auto msg = make_message(srSize + RTCP_SDES::size({{uint8_t(rtpConfig->cname.size())}}), auto msg = make_message(srSize + RTCP_SDES::Size({{uint8_t(rtpConfig->cname.size())}}),
Message::Type::Control); Message::Type::Control);
auto sr = reinterpret_cast<RTCP_SR *>(msg->data()); auto sr = reinterpret_cast<RTCP_SR *>(msg->data());
auto timestamp_s = rtpConfig->timestampToSeconds(timestamp); auto timestamp_s = rtpConfig->timestampToSeconds(timestamp);

600
src/rtp.cpp Normal file
View File

@ -0,0 +1,600 @@
/**
* Copyright (c) 2020 Staz Modrzynski
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020 Filip Klembara (in2core)
*
* 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 "rtp.hpp"
#include "impl/internals.hpp"
#include <cmath>
#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#ifndef htonll
#define htonll(x) \
((uint64_t)(((uint64_t)htonl((uint32_t)(x))) << 32) | (uint64_t)htonl((uint32_t)((x) >> 32)))
#endif
#ifndef ntohll
#define ntohll(x) htonll(x)
#endif
namespace rtc {
uint8_t RTP::version() const { return _first >> 6; }
bool RTP::padding() const { return (_first >> 5) & 0x01; }
bool RTP::extension() const { return (_first >> 4) & 0x01; }
uint8_t RTP::csrcCount() const { return _first & 0x0F; }
uint8_t RTP::marker() const { return _payloadType & 0b10000000; }
uint8_t RTP::payloadType() const { return _payloadType & 0b01111111; }
uint16_t RTP::seqNumber() const { return ntohs(_seqNumber); }
uint32_t RTP::timestamp() const { return ntohl(_timestamp); }
uint32_t RTP::ssrc() const { return ntohl(_ssrc); }
size_t RTP::getSize() const {
return reinterpret_cast<const char *>(&_csrc) - reinterpret_cast<const char *>(this) +
sizeof(SSRC) * csrcCount();
}
const char *RTP::getBody() const {
return reinterpret_cast<const char *>(&_csrc) + sizeof(SSRC) * csrcCount();
}
char *RTP::getBody() { return reinterpret_cast<char *>(&_csrc) + sizeof(SSRC) * csrcCount(); }
void RTP::preparePacket() { _first |= (1 << 7); }
void RTP::setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
void RTP::setPayloadType(uint8_t newPayloadType) {
_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
}
void RTP::setSsrc(uint32_t in_ssrc) { _ssrc = htonl(in_ssrc); }
void RTP::setMarker(bool marker) { _payloadType = (_payloadType & 0x7F) | (marker << 7); };
void RTP::setTimestamp(uint32_t i) { _timestamp = htonl(i); }
void RTP::log() const {
PLOG_VERBOSE << "RTP V: " << (int)version() << " P: " << (padding() ? "P" : " ")
<< " X: " << (extension() ? "X" : " ") << " CC: " << (int)csrcCount()
<< " M: " << (marker() ? "M" : " ") << " PT: " << (int)payloadType()
<< " SEQNO: " << seqNumber() << " TS: " << timestamp();
}
SSRC RTCP_ReportBlock::getSSRC() const { return ntohl(_ssrc); }
void RTCP_ReportBlock::preparePacket(SSRC in_ssrc, [[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets,
uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter,
uint64_t lastSR_NTP, uint64_t lastSR_DELAY) {
setSeqNo(highestSeqNo, seqNoCycles);
setJitter(jitter);
setSSRC(in_ssrc);
// Middle 32 bits of NTP Timestamp
// _lastReport = lastSR_NTP >> 16u;
setNTPOfSR(uint64_t(lastSR_NTP));
setDelaySinceSR(uint32_t(lastSR_DELAY));
// The delay, expressed in units of 1/65536 seconds
// _delaySinceLastReport = lastSR_DELAY;
}
void RTCP_ReportBlock::setSSRC(SSRC in_ssrc) { _ssrc = htonl(in_ssrc); }
void RTCP_ReportBlock::setPacketsLost([[maybe_unused]] unsigned int packetsLost,
[[maybe_unused]] unsigned int totalPackets) {
// TODO Implement loss percentages.
_fractionLostAndPacketsLost = 0;
}
unsigned int RTCP_ReportBlock::getLossPercentage() const {
// TODO Implement loss percentages.
return 0;
}
unsigned int RTCP_ReportBlock::getPacketLostCount() const {
// TODO Implement total packets lost.
return 0;
}
uint16_t RTCP_ReportBlock::seqNoCycles() const { return ntohs(_seqNoCycles); }
uint16_t RTCP_ReportBlock::highestSeqNo() const { return ntohs(_highestSeqNo); }
uint32_t RTCP_ReportBlock::jitter() const { return ntohl(_jitter); }
uint32_t RTCP_ReportBlock::delaySinceSR() const { return ntohl(_delaySinceLastReport); }
void RTCP_ReportBlock::setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
_highestSeqNo = htons(highestSeqNo);
_seqNoCycles = htons(seqNoCycles);
}
void RTCP_ReportBlock::setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
void RTCP_ReportBlock::setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
uint32_t RTCP_ReportBlock::getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
void RTCP_ReportBlock::setDelaySinceSR(uint32_t sr) {
// The delay, expressed in units of 1/65536 seconds
_delaySinceLastReport = htonl(sr);
}
void RTCP_ReportBlock::log() const {
PLOG_VERBOSE << "RTCP report block: "
<< "ssrc="
<< ntohl(_ssrc)
// TODO: Implement these reports
// << ", fractionLost=" << fractionLost
// << ", packetsLost=" << packetsLost
<< ", highestSeqNo=" << highestSeqNo() << ", seqNoCycles=" << seqNoCycles()
<< ", jitter=" << jitter() << ", lastSR=" << getNTPOfSR()
<< ", lastSRDelay=" << delaySinceSR();
}
uint8_t RTCP_HEADER::version() const { return _first >> 6; }
bool RTCP_HEADER::padding() const { return (_first >> 5) & 0x01; }
uint8_t RTCP_HEADER::reportCount() const { return _first & 0x0F; }
uint8_t RTCP_HEADER::payloadType() const { return _payloadType; }
uint16_t RTCP_HEADER::length() const { return ntohs(_length); }
size_t RTCP_HEADER::lengthInBytes() const { return (1 + length()) * 4; }
void RTCP_HEADER::setPayloadType(uint8_t type) { _payloadType = type; }
void RTCP_HEADER::setReportCount(uint8_t count) {
_first = (_first & 0b11100000u) | (count & 0b00011111u);
}
void RTCP_HEADER::setLength(uint16_t length) { _length = htons(length); }
void RTCP_HEADER::prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length) {
_first = 0b10000000; // version 2, no padding
setReportCount(reportCount);
setPayloadType(payloadType);
setLength(length);
}
void RTCP_HEADER::log() const {
PLOG_VERBOSE << "RTCP header: "
<< "version=" << unsigned(version()) << ", padding=" << padding()
<< ", reportCount=" << unsigned(reportCount())
<< ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
}
SSRC RTCP_FB_HEADER::packetSenderSSRC() const { return ntohl(_packetSender); }
SSRC RTCP_FB_HEADER::mediaSourceSSRC() const { return ntohl(_mediaSource); }
void RTCP_FB_HEADER::setPacketSenderSSRC(SSRC ssrc) { _packetSender = htonl(ssrc); }
void RTCP_FB_HEADER::setMediaSourceSSRC(SSRC ssrc) { _mediaSource = htonl(ssrc); }
void RTCP_FB_HEADER::log() const {
header.log();
PLOG_VERBOSE << "FB: "
<< " packet sender: " << packetSenderSSRC()
<< " media source: " << mediaSourceSSRC();
}
unsigned int RTCP_SR::Size(unsigned int reportCount) {
return sizeof(RTCP_HEADER) + 24 + reportCount * sizeof(RTCP_ReportBlock);
}
void RTCP_SR::preparePacket(SSRC senderSSRC, uint8_t reportCount) {
unsigned int length = ((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
header.prepareHeader(200, reportCount, uint16_t(length));
this->_senderSSRC = htonl(senderSSRC);
}
const RTCP_ReportBlock *RTCP_SR::getReportBlock(int num) const { return &_reportBlocks + num; }
RTCP_ReportBlock *RTCP_SR::getReportBlock(int num) { return &_reportBlocks + num; }
size_t RTCP_SR::getSize() const {
// "length" in packet is one less than the number of 32 bit words in the packet.
return sizeof(uint32_t) * (1 + size_t(header.length()));
}
uint64_t RTCP_SR::ntpTimestamp() const { return ntohll(_ntpTimestamp); }
uint32_t RTCP_SR::rtpTimestamp() const { return ntohl(_rtpTimestamp); }
uint32_t RTCP_SR::packetCount() const { return ntohl(_packetCount); }
uint32_t RTCP_SR::octetCount() const { return ntohl(_octetCount); }
uint32_t RTCP_SR::senderSSRC() const { return ntohl(_senderSSRC); }
void RTCP_SR::setNtpTimestamp(uint64_t ts) { _ntpTimestamp = htonll(ts); }
void RTCP_SR::setRtpTimestamp(uint32_t ts) { _rtpTimestamp = htonl(ts); }
void RTCP_SR::setOctetCount(uint32_t ts) { _octetCount = htonl(ts); }
void RTCP_SR::setPacketCount(uint32_t ts) { _packetCount = htonl(ts); }
void RTCP_SR::log() const {
header.log();
PLOG_VERBOSE << "RTCP SR: "
<< " SSRC=" << senderSSRC() << ", NTP_TS=" << ntpTimestamp()
<< ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
<< ", octetCount=" << octetCount();
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
getReportBlock(i)->log();
}
}
unsigned int RTCP_SDES_ITEM::Size(uint8_t textLength) { return textLength + 2; }
std::string RTCP_SDES_ITEM::text() const { return std::string(_text, _length); }
void RTCP_SDES_ITEM::setText(std::string text) {
if (text.size() > 0xFF)
throw std::invalid_argument("text is too long");
_length = uint8_t(text.size());
memcpy(_text, text.data(), text.size());
}
uint8_t RTCP_SDES_ITEM::length() const { return _length; }
unsigned int RTCP_SDES_CHUNK::Size(const std::vector<uint8_t> textLengths) {
unsigned int itemsSize = 0;
for (auto length : textLengths) {
itemsSize += RTCP_SDES_ITEM::Size(length);
}
auto nullTerminatedItemsSize = itemsSize + 1;
auto words = uint8_t(std::ceil(double(nullTerminatedItemsSize) / 4)) + 1;
return words * 4;
}
SSRC RTCP_SDES_CHUNK::ssrc() const { return ntohl(_ssrc); }
void RTCP_SDES_CHUNK::setSSRC(SSRC ssrc) { _ssrc = htonl(ssrc); }
const RTCP_SDES_ITEM *RTCP_SDES_CHUNK::getItem(int num) const {
auto base = &_items;
while (num-- > 0) {
auto itemSize = RTCP_SDES_ITEM::Size(base->length());
base = reinterpret_cast<const RTCP_SDES_ITEM *>(reinterpret_cast<const uint8_t *>(base) +
itemSize);
}
return reinterpret_cast<const RTCP_SDES_ITEM *>(base);
}
RTCP_SDES_ITEM *RTCP_SDES_CHUNK::getItem(int num) {
auto base = &_items;
while (num-- > 0) {
auto itemSize = RTCP_SDES_ITEM::Size(base->length());
base = reinterpret_cast<RTCP_SDES_ITEM *>(reinterpret_cast<uint8_t *>(base) + itemSize);
}
return reinterpret_cast<RTCP_SDES_ITEM *>(base);
}
unsigned int RTCP_SDES_CHUNK::getSize() const {
std::vector<uint8_t> textLengths{};
unsigned int i = 0;
auto item = getItem(i);
while (item->type != 0) {
textLengths.push_back(item->length());
item = getItem(++i);
}
return Size(textLengths);
}
long RTCP_SDES_CHUNK::safelyCountChunkSize(size_t maxChunkSize) const {
if (maxChunkSize < RTCP_SDES_CHUNK::Size({})) {
// chunk is truncated
return -1;
}
size_t size = sizeof(SSRC);
unsigned int i = 0;
// We can always access first 4 bytes of first item (in case of no items there will be 4
// null bytes)
auto item = getItem(i);
std::vector<uint8_t> textsLength{};
while (item->type != 0) {
if (size + RTCP_SDES_ITEM::Size(0) > maxChunkSize) {
// item is too short
return -1;
}
auto itemLength = item->length();
if (size + RTCP_SDES_ITEM::Size(itemLength) >= maxChunkSize) {
// item is too large (it can't be equal to chunk size because after item there
// must be 1-4 null bytes as padding)
return -1;
}
textsLength.push_back(itemLength);
// safely to access next item
item = getItem(++i);
}
auto realSize = RTCP_SDES_CHUNK::Size(textsLength);
if (realSize > maxChunkSize) {
// Chunk is too large
return -1;
}
return realSize;
}
unsigned int RTCP_SDES::Size(const std::vector<std::vector<uint8_t>> lengths) {
unsigned int chunks_size = 0;
for (auto length : lengths)
chunks_size += RTCP_SDES_CHUNK::Size(length);
return 4 + chunks_size;
}
bool RTCP_SDES::isValid() const {
auto chunksSize = header.lengthInBytes() - sizeof(header);
if (chunksSize == 0) {
return true;
}
// there is at least one chunk
unsigned int i = 0;
unsigned int size = 0;
while (size < chunksSize) {
if (chunksSize < size + RTCP_SDES_CHUNK::Size({})) {
// chunk is truncated
return false;
}
auto chunk = getChunk(i++);
auto chunkSize = chunk->safelyCountChunkSize(chunksSize - size);
if (chunkSize < 0) {
// chunk is invalid
return false;
}
size += chunkSize;
}
return size == chunksSize;
}
unsigned int RTCP_SDES::chunksCount() const {
if (!isValid()) {
return 0;
}
uint16_t chunksSize = 4 * (header.length() + 1) - sizeof(header);
unsigned int size = 0;
unsigned int i = 0;
while (size < chunksSize) {
size += getChunk(i++)->getSize();
}
return i;
}
const RTCP_SDES_CHUNK *RTCP_SDES::getChunk(int num) const {
auto base = &_chunks;
while (num-- > 0) {
auto chunkSize = base->getSize();
base = reinterpret_cast<const RTCP_SDES_CHUNK *>(reinterpret_cast<const uint8_t *>(base) +
chunkSize);
}
return reinterpret_cast<const RTCP_SDES_CHUNK *>(base);
}
RTCP_SDES_CHUNK *RTCP_SDES::getChunk(int num) {
auto base = &_chunks;
while (num-- > 0) {
auto chunkSize = base->getSize();
base = reinterpret_cast<RTCP_SDES_CHUNK *>(reinterpret_cast<uint8_t *>(base) + chunkSize);
}
return reinterpret_cast<RTCP_SDES_CHUNK *>(base);
}
void RTCP_SDES::preparePacket(uint8_t chunkCount) {
unsigned int chunkSize = 0;
for (uint8_t i = 0; i < chunkCount; i++) {
auto chunk = getChunk(i);
chunkSize += chunk->getSize();
}
uint16_t length = uint16_t((sizeof(header) + chunkSize) / 4 - 1);
header.prepareHeader(202, chunkCount, length);
}
const RTCP_ReportBlock *RTCP_RR::getReportBlock(int num) const { return &_reportBlocks + num; }
RTCP_ReportBlock *RTCP_RR::getReportBlock(int num) { return &_reportBlocks + num; }
size_t RTCP_RR::SizeWithReportBlocks(uint8_t reportCount) {
return sizeof(header) + 4 + size_t(reportCount) * sizeof(RTCP_ReportBlock);
}
SSRC RTCP_RR::senderSSRC() const { return ntohl(_senderSSRC); }
bool RTCP_RR::isSenderReport() { return header.payloadType() == 200; }
bool RTCP_RR::isReceiverReport() { return header.payloadType() == 201; }
size_t RTCP_RR::getSize() const {
// "length" in packet is one less than the number of 32 bit words in the packet.
return sizeof(uint32_t) * (1 + size_t(header.length()));
}
void RTCP_RR::preparePacket(SSRC senderSSRC, uint8_t reportCount) {
// "length" in packet is one less than the number of 32 bit words in the packet.
size_t length = (SizeWithReportBlocks(reportCount) / 4) - 1;
header.prepareHeader(201, reportCount, uint16_t(length));
this->_senderSSRC = htonl(senderSSRC);
}
void RTCP_RR::setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); }
void RTCP_RR::log() const {
header.log();
PLOG_VERBOSE << "RTCP RR: "
<< " SSRC=" << ntohl(_senderSSRC);
for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
getReportBlock(i)->log();
}
}
size_t RTCP_REMB::SizeWithSSRCs(int count) {
return sizeof(RTCP_REMB) + (count - 1) * sizeof(SSRC);
}
unsigned int RTCP_REMB::getSize() const {
// "length" in packet is one less than the number of 32 bit words in the packet.
return sizeof(uint32_t) * (1 + header.header.length());
}
void RTCP_REMB::preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate) {
// Report Count becomes the format here.
header.header.prepareHeader(206, 15, 0);
// Always zero.
header.setMediaSourceSSRC(0);
header.setPacketSenderSSRC(senderSSRC);
_id[0] = 'R';
_id[1] = 'E';
_id[2] = 'M';
_id[3] = 'B';
setBitrate(numSSRC, in_bitrate);
}
void RTCP_REMB::setBitrate(unsigned int numSSRC, unsigned int in_bitrate) {
unsigned int exp = 0;
while (in_bitrate > pow(2, 18) - 1) {
exp++;
in_bitrate /= 2;
}
// "length" in packet is one less than the number of 32 bit words in the packet.
header.header.setLength(
uint16_t((offsetof(RTCP_REMB, _ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
_bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | in_bitrate);
}
void RTCP_REMB::setSsrc(int iterator, SSRC newSssrc) { _ssrc[iterator] = htonl(newSssrc); }
unsigned int RTCP_PLI::Size() { return sizeof(RTCP_FB_HEADER); }
void RTCP_PLI::preparePacket(SSRC messageSSRC) {
header.header.prepareHeader(206, 1, 2);
header.setPacketSenderSSRC(messageSSRC);
header.setMediaSourceSSRC(messageSSRC);
}
void RTCP_PLI::log() const { header.log(); }
unsigned int RTCP_FIR::Size() { return sizeof(RTCP_FB_HEADER) + sizeof(RTCP_FIR_PART); }
void RTCP_FIR::preparePacket(SSRC messageSSRC, uint8_t seqNo) {
header.header.prepareHeader(206, 4, 2 + 2 * 1);
header.setPacketSenderSSRC(messageSSRC);
header.setMediaSourceSSRC(messageSSRC);
parts[0].ssrc = htonl(messageSSRC);
parts[0].seqNo = seqNo;
}
void RTCP_FIR::log() const { header.log(); }
uint16_t RTCP_NACK_PART::pid() { return ntohs(_pid); }
uint16_t RTCP_NACK_PART::blp() { return ntohs(_blp); }
void RTCP_NACK_PART::setPid(uint16_t pid) { _pid = htons(pid); }
void RTCP_NACK_PART::setBlp(uint16_t blp) { _blp = htons(blp); }
std::vector<uint16_t> RTCP_NACK_PART::getSequenceNumbers() {
std::vector<uint16_t> result{};
result.reserve(17);
uint16_t p = pid();
result.push_back(p);
uint16_t bitmask = blp();
uint16_t i = p + 1;
while (bitmask > 0) {
if (bitmask & 0x1) {
result.push_back(i);
}
i += 1;
bitmask >>= 1;
}
return result;
}
unsigned int RTCP_NACK::Size(unsigned int discreteSeqNoCount) {
return offsetof(RTCP_NACK, parts) + sizeof(RTCP_NACK_PART) * discreteSeqNoCount;
}
unsigned int RTCP_NACK::getSeqNoCount() { return header.header.length() - 2; }
void RTCP_NACK::preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
header.header.prepareHeader(205, 1, 2 + uint16_t(discreteSeqNoCount));
header.setMediaSourceSSRC(ssrc);
header.setPacketSenderSSRC(ssrc);
}
bool RTCP_NACK::addMissingPacket(unsigned int *fciCount, uint16_t *fciPID, uint16_t missingPacket) {
if (*fciCount == 0 || missingPacket < *fciPID || missingPacket > (*fciPID + 16)) {
parts[*fciCount].setPid(missingPacket);
parts[*fciCount].setBlp(0);
*fciPID = missingPacket;
(*fciCount)++;
return true;
} else {
// TODO SPEED!
uint16_t blp = parts[(*fciCount) - 1].blp();
uint16_t newBit = uint16_t(1u << (missingPacket - (1 + *fciPID)));
parts[(*fciCount) - 1].setBlp(blp | newBit);
return false;
}
}
uint16_t RTP_RTX::getOriginalSeqNo() const { return ntohs(*(uint16_t *)(header.getBody())); }
const char *RTP_RTX::getBody() const { return header.getBody() + sizeof(uint16_t); }
char *RTP_RTX::getBody() { return header.getBody() + sizeof(uint16_t); }
size_t RTP_RTX::getBodySize(size_t totalSize) const {
return totalSize - (getBody() - reinterpret_cast<const char *>(this));
}
size_t RTP_RTX::getSize() const { return header.getSize() + sizeof(uint16_t); }
size_t RTP_RTX::normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
header.setSeqNumber(getOriginalSeqNo());
header.setSsrc(originalSSRC);
header.setPayloadType(originalPayloadType);
// TODO, the -12 is the size of the header (which is variable!)
memmove(header.getBody(), getBody(), totalSize - getSize());
return totalSize - 2;
}
size_t RTP_RTX::copyTo(RTP *dest, size_t totalSize, uint8_t originalPayloadType) {
memmove((char *)dest, (char *)this, header.getSize());
dest->setSeqNumber(getOriginalSeqNo());
dest->setPayloadType(originalPayloadType);
memmove(dest->getBody(), getBody(), getBodySize(totalSize));
return totalSize;
}
}; // namespace rtc

View File

@ -1,25 +1,27 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "rtppacketizationconfig.hpp" #include "rtppacketizationconfig.hpp"
#include <cassert>
namespace rtc { namespace rtc {
RtpPacketizationConfig::RtpPacketizationConfig(SSRC ssrc, string cname, uint8_t payloadType, RtpPacketizationConfig::RtpPacketizationConfig(SSRC ssrc, string cname, uint8_t payloadType,

View File

@ -1,25 +1,27 @@
/* /**
* libdatachannel streamer example
* Copyright (c) 2020 Filip Klembara (in2core) * Copyright (c) 2020 Filip Klembara (in2core)
* *
* This program is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU Lesser General Public
* as published by the Free Software Foundation; either version 2 * License as published by the Free Software Foundation; either
* of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Lesser General Public
* along with this program; If not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if RTC_ENABLE_MEDIA #if RTC_ENABLE_MEDIA
#include "rtppacketizer.hpp" #include "rtppacketizer.hpp"
#include <cstring>
namespace rtc { namespace rtc {
RtpPacketizer::RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig) RtpPacketizer::RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)
@ -37,7 +39,7 @@ binary_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool setMark) {
rtp->setMarker(true); rtp->setMarker(true);
} }
rtp->preparePacket(); rtp->preparePacket();
memcpy(msg->data() + rtpHeaderSize, payload->data(), payload->size()); std::memcpy(msg->data() + rtpHeaderSize, payload->data(), payload->size());
return msg; return msg;
} }

View File

@ -17,8 +17,8 @@
*/ */
#include "track.hpp" #include "track.hpp"
#include "globals.hpp"
#include "impl/internals.hpp"
#include "impl/track.hpp" #include "impl/track.hpp"
namespace rtc { namespace rtc {
@ -46,9 +46,7 @@ bool Track::isOpen(void) const { return impl()->isOpen(); }
bool Track::isClosed(void) const { return impl()->isClosed(); } bool Track::isClosed(void) const { return impl()->isClosed(); }
size_t Track::maxMessageSize() const { size_t Track::maxMessageSize() const { return impl()->maxMessageSize(); }
return impl()->maxMessageSize();
}
void Track::setMediaHandler(shared_ptr<MediaHandler> handler) { void Track::setMediaHandler(shared_ptr<MediaHandler> handler) {
impl()->setMediaHandler(std::move(handler)); impl()->setMediaHandler(std::move(handler));

View File

@ -19,10 +19,10 @@
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
#include "websocket.hpp" #include "websocket.hpp"
#include "globals.hpp"
#include "common.hpp" #include "common.hpp"
#include "impl/websocket.hpp" #include "impl/websocket.hpp"
#include "impl/internals.hpp"
#include <regex> #include <regex>

View File

@ -122,11 +122,10 @@ size_t benchmark(milliseconds duration) {
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Send failed: " << e.what() << std::endl; std::cout << "Send failed: " << e.what() << std::endl;
} }
});
// When sent data is buffered in the DataChannel, // When sent data is buffered in the DataChannel,
// wait for onBufferedAmountLow callback to continue // wait for onBufferedAmountLow callback to continue
});
dc1->onBufferedAmountLow([wdc1 = make_weak_ptr(dc1), &messageData]() { dc1->onBufferedAmountLow([wdc1 = make_weak_ptr(dc1), &messageData]() {
auto dc1 = wdc1.lock(); auto dc1 = wdc1.lock();
if (!dc1) if (!dc1)

View File

@ -137,11 +137,11 @@ static void RTC_API dataChannelCallback(int pc, int dc, void *ptr) {
return; return;
} }
rtcSetOpenCallback(dc, openCallback);
rtcSetClosedCallback(dc, closedCallback); rtcSetClosedCallback(dc, closedCallback);
rtcSetMessageCallback(dc, messageCallback); rtcSetMessageCallback(dc, messageCallback);
peer->dc = dc; peer->dc = dc;
peer->connected = true;
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2"; const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string

View File

@ -83,7 +83,7 @@ static void RTC_API closedCallback(int id, void *ptr) {
static void RTC_API trackCallback(int pc, int tr, void *ptr) { static void RTC_API trackCallback(int pc, int tr, void *ptr) {
Peer *peer = (Peer *)ptr; Peer *peer = (Peer *)ptr;
peer->tr = tr; peer->tr = tr;
peer->connected = true; rtcSetOpenCallback(tr, openCallback);
rtcSetClosedCallback(tr, closedCallback); rtcSetClosedCallback(tr, closedCallback);
char buffer[1024]; char buffer[1024];

View File

@ -24,6 +24,8 @@
#include <memory> #include <memory>
#include <thread> #include <thread>
#define CUSTOM_MAX_MESSAGE_SIZE 1048576
using namespace rtc; using namespace rtc;
using namespace std; using namespace std;
@ -39,7 +41,7 @@ void test_connectivity() {
// Custom MTU example // Custom MTU example
config1.mtu = 1500; config1.mtu = 1500;
// Custom max message size // Custom max message size
config1.maxMessageSize = 1048576; config1.maxMessageSize = CUSTOM_MAX_MESSAGE_SIZE;
PeerConnection pc1(config1); PeerConnection pc1(config1);
@ -50,7 +52,7 @@ void test_connectivity() {
// Custom MTU example // Custom MTU example
config2.mtu = 1500; config2.mtu = 1500;
// Custom max message size // Custom max message size
config2.maxMessageSize = 1048576; config2.maxMessageSize = CUSTOM_MAX_MESSAGE_SIZE;
// Port range example // Port range example
config2.portRangeBegin = 5000; config2.portRangeBegin = 5000;
config2.portRangeEnd = 6000; config2.portRangeEnd = 6000;
@ -105,26 +107,29 @@ void test_connectivity() {
return; return;
} }
dc->onOpen([wdc = make_weak_ptr(dc)]() {
if (auto dc = wdc.lock())
dc->send("Hello from 2");
});
dc->onMessage([](variant<binary, string> message) { dc->onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "Message 2: " << get<string>(message) << endl; cout << "Message 2: " << get<string>(message) << endl;
} }
}); });
dc->send("Hello from 2");
std::atomic_store(&dc2, dc); std::atomic_store(&dc2, dc);
}); });
auto dc1 = pc1.createDataChannel("test"); auto dc1 = pc1.createDataChannel("test");
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
auto dc1 = wdc1.lock();
if (!dc1)
return;
dc1->onOpen([wdc1 = make_weak_ptr(dc1)]() {
if (auto dc1 = wdc1.lock()) {
cout << "DataChannel 1: Open" << endl; cout << "DataChannel 1: Open" << endl;
dc1->send("Hello from 1"); dc1->send("Hello from 1");
}
}); });
dc1->onMessage([](const variant<binary, string> &message) { dc1->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "Message 1: " << get<string>(message) << endl; cout << "Message 1: " << get<string>(message) << endl;
@ -144,7 +149,7 @@ void test_connectivity() {
if (!adc2 || !adc2->isOpen() || !dc1->isOpen()) if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
throw runtime_error("DataChannel is not open"); throw runtime_error("DataChannel is not open");
if (dc1->maxMessageSize() != 1048576 || dc2->maxMessageSize() != 1048576) if (dc1->maxMessageSize() != CUSTOM_MAX_MESSAGE_SIZE || dc2->maxMessageSize() != CUSTOM_MAX_MESSAGE_SIZE)
throw runtime_error("DataChannel max message size is incorrect"); throw runtime_error("DataChannel max message size is incorrect");
if (auto addr = pc1.localAddress()) if (auto addr = pc1.localAddress())
@ -175,25 +180,26 @@ void test_connectivity() {
return; return;
} }
dc->onOpen([wdc = make_weak_ptr(dc)]() {
if (auto dc = wdc.lock())
dc->send("Second hello from 2");
});
dc->onMessage([](variant<binary, string> message) { dc->onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "Second Message 2: " << get<string>(message) << endl; cout << "Second Message 2: " << get<string>(message) << endl;
} }
}); });
dc->send("Send hello from 2");
std::atomic_store(&second2, dc); std::atomic_store(&second2, dc);
}); });
auto second1 = pc1.createDataChannel("second"); auto second1 = pc1.createDataChannel("second");
second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() { second1->onOpen([wsecond1 = make_weak_ptr(dc1)]() {
auto second1 = wsecond1.lock(); if (auto second1 = wsecond1.lock()) {
if (!second1)
return;
cout << "Second DataChannel 1: Open" << endl; cout << "Second DataChannel 1: Open" << endl;
second1->send("Second hello from 1"); second1->send("Second hello from 1");
}
}); });
dc1->onMessage([](const variant<binary, string> &message) { dc1->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {

View File

@ -108,14 +108,17 @@ void test_turn_connectivity() {
return; return;
} }
dc->onOpen([wdc = make_weak_ptr(dc)]() {
if (auto dc = wdc.lock())
dc->send("Hello from 2");
});
dc->onMessage([](variant<binary, string> message) { dc->onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "Message 2: " << get<string>(message) << endl; cout << "Message 2: " << get<string>(message) << endl;
} }
}); });
dc->send("Hello from 2");
std::atomic_store(&dc2, dc); std::atomic_store(&dc2, dc);
}); });
@ -175,14 +178,17 @@ void test_turn_connectivity() {
return; return;
} }
dc->onOpen([wdc = make_weak_ptr(dc)]() {
if (auto dc = wdc.lock())
dc->send("Second hello from 2");
});
dc->onMessage([](variant<binary, string> message) { dc->onMessage([](variant<binary, string> message) {
if (holds_alternative<string>(message)) { if (holds_alternative<string>(message)) {
cout << "Second Message 2: " << get<string>(message) << endl; cout << "Second Message 2: " << get<string>(message) << endl;
} }
}); });
dc->send("Send hello from 2");
std::atomic_store(&second2, dc); std::atomic_store(&second2, dc);
}); });