Browse Source

Merge branch 'master' into fix_typos

Paul-Louis Ageneau 4 years ago
parent
commit
a4ca6858a1
100 changed files with 4851 additions and 1813 deletions
  1. 1 1
      .github/dependabot.yml
  2. 1 1
      .gitignore
  3. 71 0
      BUILDING.md
  4. 112 50
      CMakeLists.txt
  5. 645 0
      DOC.md
  6. 22 7
      Makefile
  7. 26 92
      README.md
  8. 1 1
      deps/libjuice
  9. 1 1
      deps/usrsctp
  10. 4 3
      examples/README.md
  11. 10 3
      examples/client/CMakeLists.txt
  12. 8 7
      examples/client/main.cpp
  13. 12 0
      examples/copy-paste-capi/CMakeLists.txt
  14. 12 0
      examples/copy-paste/CMakeLists.txt
  15. 7 0
      examples/media/CMakeLists.txt
  16. 1 1
      examples/media/main.cpp
  17. 7 0
      examples/sfu-media/CMakeLists.txt
  18. 1 1
      examples/sfu-media/main.cpp
  19. 339 0
      examples/signaling-server-nodejs/LICENSE
  20. 285 0
      examples/signaling-server-nodejs/package-lock.json
  21. 4 4
      examples/signaling-server-nodejs/package.json
  22. 10 38
      examples/signaling-server-nodejs/signaling-server.js
  23. 27 6
      examples/streamer/CMakeLists.txt
  24. 4 4
      examples/streamer/README.md
  25. 4 6
      examples/streamer/client.js
  26. 1 1
      examples/streamer/helpers.cpp
  27. 2 2
      examples/streamer/helpers.hpp
  28. 0 1
      examples/streamer/index.html
  29. 26 14
      examples/streamer/main.cpp
  30. 9 0
      examples/web/README.md
  31. 0 136
      examples/web/package-lock.json
  32. 4 4
      include/rtc/candidate.hpp
  33. 12 27
      include/rtc/channel.hpp
  34. 80 0
      include/rtc/common.hpp
  35. 7 5
      include/rtc/configuration.hpp
  36. 20 60
      include/rtc/datachannel.hpp
  37. 24 27
      include/rtc/description.hpp
  38. 3 37
      include/rtc/h264packetizationhandler.hpp
  39. 26 5
      include/rtc/h264rtppacketizer.hpp
  40. 4 4
      include/rtc/init.hpp
  41. 1 1
      include/rtc/log.hpp
  42. 55 0
      include/rtc/mediachainablehandler.hpp
  43. 5 5
      include/rtc/mediahandler.hpp
  44. 111 0
      include/rtc/mediahandlerelement.hpp
  45. 45 0
      include/rtc/mediahandlerrootelement.hpp
  46. 7 10
      include/rtc/message.hpp
  47. 12 13
      include/rtc/nalunit.hpp
  48. 3 13
      include/rtc/opuspacketizationhandler.hpp
  49. 10 4
      include/rtc/opusrtppacketizer.hpp
  50. 17 106
      include/rtc/peerconnection.hpp
  51. 2 3
      include/rtc/reliability.hpp
  52. 37 20
      include/rtc/rtc.h
  53. 15 4
      include/rtc/rtc.hpp
  54. 94 0
      include/rtc/rtcpnackresponder.hpp
  55. 3 3
      include/rtc/rtcpreceivingsession.hpp
  56. 8 31
      include/rtc/rtcpsrreporter.hpp
  57. 41 15
      include/rtc/rtp.hpp
  58. 3 3
      include/rtc/rtppacketizationconfig.hpp
  59. 3 3
      include/rtc/rtppacketizer.hpp
  60. 17 34
      include/rtc/track.hpp
  61. 33 56
      include/rtc/utils.hpp
  62. 16 46
      include/rtc/websocket.hpp
  63. 15 9
      src/candidate.cpp
  64. 201 122
      src/capi.cpp
  65. 31 42
      src/channel.cpp
  66. 30 11
      src/configuration.cpp
  67. 22 294
      src/datachannel.cpp
  68. 26 21
      src/description.cpp
  69. 41 0
      src/globals.hpp
  70. 1 140
      src/h264packetizationhandler.cpp
  71. 130 2
      src/h264rtppacketizer.cpp
  72. 2 2
      src/impl/base64.cpp
  73. 4 4
      src/impl/base64.hpp
  74. 3 15
      src/impl/certificate.cpp
  75. 10 10
      src/impl/certificate.hpp
  76. 57 0
      src/impl/channel.cpp
  77. 56 0
      src/impl/channel.hpp
  78. 365 0
      src/impl/datachannel.cpp
  79. 94 0
      src/impl/datachannel.hpp
  80. 6 5
      src/impl/dtlssrtptransport.cpp
  81. 7 7
      src/impl/dtlssrtptransport.hpp
  82. 23 20
      src/impl/dtlstransport.cpp
  83. 8 7
      src/impl/dtlstransport.hpp
  84. 40 44
      src/impl/icetransport.cpp
  85. 9 9
      src/impl/icetransport.hpp
  86. 2 2
      src/impl/logcounter.cpp
  87. 3 3
      src/impl/logcounter.hpp
  88. 1000 0
      src/impl/peerconnection.cpp
  89. 134 0
      src/impl/peerconnection.hpp
  90. 2 2
      src/impl/processor.cpp
  91. 5 5
      src/impl/processor.hpp
  92. 18 19
      src/impl/queue.hpp
  93. 69 43
      src/impl/sctptransport.cpp
  94. 17 14
      src/impl/sctptransport.hpp
  95. 5 4
      src/impl/tcptransport.cpp
  96. 5 5
      src/impl/tcptransport.hpp
  97. 30 25
      src/impl/threadpool.cpp
  98. 8 7
      src/impl/threadpool.hpp
  99. 0 0
      src/impl/tls.cpp
  100. 1 1
      src/impl/tls.hpp

+ 1 - 1
.github/dependabot.yml

@@ -1,6 +1,6 @@
 version: 2
 version: 2
 updates:
 updates:
   - package-ecosystem: "npm"
   - package-ecosystem: "npm"
-    directory: "examples/web"
+    directory: "examples/signaling-server-nodejs"
     schedule:
     schedule:
       interval: "weekly"
       interval: "weekly"

+ 1 - 1
.gitignore

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

+ 71 - 0
BUILDING.md

@@ -0,0 +1,71 @@
+# libdatachannel - Building instructions
+
+## Clone repository and submodules
+
+```bash
+$ git clone https://github.com/paullouisageneau/libdatachannel.git
+$ cd libdatachannel
+$ git submodule update --init --recursive
+```
+
+## Build with CMake
+
+The CMake library targets `libdatachannel` and `libdatachannel-static` respectively correspond to the shared and static libraries. The default target will build tests and examples. The option `USE_GNUTLS` allows to switch between OpenSSL (default) and GnuTLS, and the option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice.
+
+If you only need Data Channels, the option `NO_MEDIA` allows to make the library lighter by removing media support. Similarly, `NO_WEBSOCKET` removes WebSocket support.
+
+### POSIX-compliant operating systems (including Linux and Apple macOS)
+
+```bash
+$ cmake -B build -DUSE_GNUTLS=1 -DUSE_NICE=0
+$ cd build
+$ make -j2
+```
+
+### Apple macOS with XCode project
+
+```bash
+$ cmake -B "$BUILD_DIR" -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode
+```
+
+Xcode project is generated in *build/* directory.
+
+#### Solving **Could NOT find OpenSSL** error
+
+You need to add OpenSSL root directory if your build fails with the following message:
+
+```
+Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the
+system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY
+OPENSSL_INCLUDE_DIR)
+```
+
+for example:
+```bash
+$ cmake -B build -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl\@1.1/1.1.1h/
+```
+
+### Microsoft Windows with MinGW cross-compilation
+```bash
+$ cmake -B build -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
+$ cd build
+$ make -j2
+```
+
+### Microsoft Windows with Microsoft Visual C++
+```bash
+$ cmake -B build -G "NMake Makefiles"
+$ cd build
+$ nmake
+```
+
+## Build directly with Make (Linux only)
+
+The option `USE_GNUTLS` allows to switch between OpenSSL (default) and GnuTLS, and the option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice.
+
+If you only need Data Channels, the option `NO_MEDIA` removes media support. Similarly, `NO_WEBSOCKET` removes WebSocket support.
+
+```bash
+$ make USE_GNUTLS=1 USE_NICE=0
+```
+

+ 112 - 50
CMakeLists.txt

@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.7)
 cmake_minimum_required(VERSION 3.7)
 project(libdatachannel
 project(libdatachannel
-	VERSION 0.10.5
+	VERSION 0.11.10
 	LANGUAGES CXX)
 	LANGUAGES CXX)
 set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
 set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
 
 
@@ -46,43 +46,30 @@ endif()
 
 
 set(LIBDATACHANNEL_SOURCES
 set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/candidate.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/candidate.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/certificate.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/channel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/channel.cpp
 	${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/dtlssrtptransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/dtlstransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/icetransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/init.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/init.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/log.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/logcounter.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpreceivingsession.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpreceivingsession.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/sctptransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/threadpool.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/tls.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/processor.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/websocket.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsenderreporter.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/opusrtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/opusrtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/opuspacketizationhandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/opuspacketizationhandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264packetizationhandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264packetizationhandler.cpp
-)
-
-set(LIBDATACHANNEL_WEBSOCKET_SOURCES
-	${CMAKE_CURRENT_SOURCE_DIR}/src/base64.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/tcptransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/tlstransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/verifiedtlstransport.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/websocket.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/wstransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/mediachainablehandler.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerelement.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerrootelement.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpnackresponder.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
 )
 )
 
 
 set(LIBDATACHANNEL_HEADERS
 set(LIBDATACHANNEL_HEADERS
@@ -92,14 +79,13 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/configuration.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/configuration.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/datachannel.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/datachannel.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/description.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/description.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcphandler.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/include.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/init.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/init.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/log.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/queue.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.hpp
@@ -107,13 +93,62 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsenderreportable.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opusrtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opusrtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opuspacketizationhandler.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opuspacketizationhandler.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264packetizationhandler.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264packetizationhandler.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediachainablehandler.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandlerelement.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandlerrootelement.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpnackresponder.hpp
+)
+
+set(LIBDATACHANNEL_IMPL_SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/certificate.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/channel.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/datachannel.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/threadpool.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tls.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/track.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/processor.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/base64.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/verifiedtlstransport.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/websocket.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/wstransport.cpp
+)
+
+set(LIBDATACHANNEL_IMPL_HEADERS
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/certificate.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/channel.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/datachannel.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/queue.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sctptransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/threadpool.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tls.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/track.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/processor.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/base64.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/verifiedtlstransport.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/websocket.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/wstransport.hpp
 )
 )
 
 
 set(TESTS_SOURCES
 set(TESTS_SOURCES
@@ -127,6 +162,26 @@ set(TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/test/benchmark.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/benchmark.cpp
 )
 )
 
 
+set(TESTS_UWP_RESOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Logo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/package.appxManifest
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SmallLogo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SmallLogo44x44.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/SplashScreen.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/StoreLogo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/tests/Windows_TemporaryKey.pfx
+)
+
+set(BENCHMARK_UWP_RESOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Logo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/package.appxManifest
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SmallLogo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SmallLogo44x44.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/SplashScreen.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/StoreLogo.png
+	${CMAKE_CURRENT_SOURCE_DIR}/test/uwp/benchmark/Windows_TemporaryKey.pfx
+)
+
 set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
 set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
 set(THREADS_PREFER_PTHREAD_FLAG TRUE)
 set(THREADS_PREFER_PTHREAD_FLAG TRUE)
 find_package(Threads REQUIRED)
 find_package(Threads REQUIRED)
@@ -145,23 +200,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
 endif()
 endif()
 add_library(Usrsctp::Usrsctp ALIAS usrsctp)
 add_library(Usrsctp::Usrsctp ALIAS usrsctp)
 
 
-if (NO_WEBSOCKET)
-	add_library(datachannel SHARED
-		${LIBDATACHANNEL_SOURCES})
-	add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
-		${LIBDATACHANNEL_SOURCES})
-	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
-	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
-else()
-	add_library(datachannel SHARED
-		${LIBDATACHANNEL_SOURCES}
-		${LIBDATACHANNEL_WEBSOCKET_SOURCES})
-	add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
-		${LIBDATACHANNEL_SOURCES}
-		${LIBDATACHANNEL_WEBSOCKET_SOURCES})
-	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
-	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
-endif()
+add_library(datachannel SHARED
+	${LIBDATACHANNEL_SOURCES}
+	${LIBDATACHANNEL_HEADERS}
+	${LIBDATACHANNEL_IMPL_SOURCES}
+	${LIBDATACHANNEL_IMPL_HEADERS})
+add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
+	${LIBDATACHANNEL_SOURCES}
+	${LIBDATACHANNEL_HEADERS}
+	${LIBDATACHANNEL_IMPL_SOURCES}
+	${LIBDATACHANNEL_IMPL_HEADERS})
 
 
 set_target_properties(datachannel PROPERTIES
 set_target_properties(datachannel PROPERTIES
 	VERSION ${PROJECT_VERSION}
 	VERSION ${PROJECT_VERSION}
@@ -187,6 +235,14 @@ if(WIN32)
 	target_link_libraries(datachannel-static PUBLIC ws2_32) # winsock2
 	target_link_libraries(datachannel-static PUBLIC ws2_32) # winsock2
 endif()
 endif()
 
 
+if (NO_WEBSOCKET)
+	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
+	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
+else()
+	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
+	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
+endif()
+
 if(NO_MEDIA)
 if(NO_MEDIA)
 	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=0)
@@ -284,24 +340,30 @@ endif()
 
 
 # Tests
 # Tests
 if(NOT NO_TESTS)
 if(NOT NO_TESTS)
-	add_executable(datachannel-tests ${TESTS_SOURCES})
+	if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+		# Add resource files needed for UWP apps.
+		add_executable(datachannel-tests ${TESTS_SOURCES} ${TESTS_UWP_RESOURCES})
+	else()
+		add_executable(datachannel-tests ${TESTS_SOURCES})
+	endif()
 	set_target_properties(datachannel-tests PROPERTIES
 	set_target_properties(datachannel-tests PROPERTIES
 		VERSION ${PROJECT_VERSION}
 		VERSION ${PROJECT_VERSION}
 		CXX_STANDARD 17)
 		CXX_STANDARD 17)
-	if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") # Prevent a bug in manifest generation for UWP
-		set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
-	endif()
+	set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
 	target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
 	target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
 	target_link_libraries(datachannel-tests datachannel)
 	target_link_libraries(datachannel-tests datachannel)
 
 
 	# Benchmark
 	# Benchmark
-	add_executable(datachannel-benchmark test/benchmark.cpp)
+	if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+		# Add resource files needed for UWP apps.
+		add_executable(datachannel-benchmark test/benchmark.cpp ${BENCHMARK_UWP_RESOURCES})
+	else()
+		add_executable(datachannel-benchmark test/benchmark.cpp)
+	endif()
 	set_target_properties(datachannel-benchmark PROPERTIES
 	set_target_properties(datachannel-benchmark PROPERTIES
 		VERSION ${PROJECT_VERSION}
 		VERSION ${PROJECT_VERSION}
 		CXX_STANDARD 17)
 		CXX_STANDARD 17)
-	if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") # Prevent a bug in manifest generation for UWP
-		set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
-	endif()
+	set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
 	target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
 	target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
 	target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
 	target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
 	target_link_libraries(datachannel-benchmark datachannel)
 	target_link_libraries(datachannel-benchmark datachannel)

+ 645 - 0
DOC.md

@@ -0,0 +1,645 @@
+# libdatachannel - C API Documentation
+
+The following details the C API of libdatachannel. The C API is available by including the `rtc/rtc.h` header.
+
+### General considerations
+
+Unless stated otherwise, functions return `RTC_ERR_SUCCESS`, defined as `0`, on success.
+
+All functions can return the following negative error codes:
+- `RTC_ERR_INVALID`: an invalid argument was provided
+- `RTC_ERR_FAILURE`: a runtime error happened
+- `RTC_ERR_NOT_AVAIL`: an element is not available at the moment
+- `RTC_ERR_TOO_SMALL`: a user-provided buffer is too small
+
+All functions taking pointers as arguments (excepted the opaque user pointer) need the memory to be accessible until the call returns, but it can be safely discarded afterwards.
+
+### Common
+
+#### rtcInitLogger
+
+```
+void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb)
+```
+
+Arguments:
+- `level`: the log level. It must be one of the following: `RTC_LOG_NONE`, `RTC_LOG_FATAL`, `RTC_LOG_ERROR`, `RTC_LOG_WARNING`, `RTC_LOG_INFO`, `RTC_LOG_DEBUG`, `RTC_LOG_VERBOSE`.
+- `cb` (optional): the callback to pass the log lines to. If the callback is already set, it is replaced. If NULL after a callback is set, the callback is unset. If NULL on first call, the library will log to stdout instead.
+
+`cb` must have the following signature:
+`void myLogCallback(rtcLogLevel level, const char *message)`
+
+Arguments:
+- `level`: the log level for the current message. It will be one of the following: `RTC_LOG_FATAL`, `RTC_LOG_ERROR`, `RTC_LOG_WARNING`, `RTC_LOG_INFO`, `RTC_LOG_DEBUG`, `RTC_LOG_VERBOSE`.
+- `message`: a null-terminated string containing the log message
+
+#### rtcPreload
+
+```
+void rtcPreload(void)
+```
+
+An optional call to `rtcPreload` preloads the global resources used by the library. If it is not called, resources are lazy-loaded when they are required for the first time by a PeerConnection, which for instance prevents from properly timing connection establishment (as the first one will take way more time). The call blocks until preloading is finished. If resources are already loaded, the call has no effect.
+
+#### rtcCleanup
+
+```
+void rtcCleanup(void)
+```
+
+An optional call to `rtcCleanup` requests unloading of the global resources used by the library. If all created PeerConnections are deleted, unloading will happen immediately and the call will block until unloading is done, otherwise unloading will happen as soon as the last PeerConnection is deleted. If resources are already unloaded, the call has no effect.
+
+#### rtcSetUserPointer
+
+```
+void rtcSetUserPointer(int id, void *user_ptr)
+```
+
+Sets a opaque user pointer for a Peer Connection, Data Channel, Track, or WebSocket. The user pointer will be passed as last argument in each corresponding callback. It will never be accessed in any way. The initial user pointer of a Peer Connection or WebSocket is `NULL`, and the initial one of a Data Channel or Track is the one of the Peer Connection at the time of creation.
+
+Arguments:
+- `id`: the identifier of Peer Connection, Data Channel, Track, or WebSocket
+- `user_ptr`: an opaque pointer whose meaning is up to the user
+
+### PeerConnection
+
+#### rtcCreatePeerConnection
+
+```
+int rtcCreatePeerConnection(const rtcConfiguration *config)
+
+typedef struct {
+	const char **iceServers;
+	int iceServersCount;
+	bool enableIceTcp;
+	bool disableAutoNegotiation;
+	uint16_t portRangeBegin;
+	uint16_t portRangeEnd;
+	int mtu;
+} rtcConfiguration;
+```
+
+Creates a Peer Connection.
+
+Arguments:
+- `config`: the configuration structure, containing:
+  - `iceServers` (optional): an array of pointers on null-terminated ice server URIs (NULL if unused)
+  - `iceServersCount` (optional): number of URLs in the array pointed by `iceServers` (0 if unused)
+  - `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
+  - `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)
+  - `mtu` (optional): manually set the Maximum Transfer Unit (MTU) for the connection (0 if automatic)
+
+Return value: the identifier of the new Peer Connection or a negative error code.
+
+The Peer Connection must be deleted with `rtcDeletePeerConnection`.
+
+The format of each entry in `iceServers` must match the format `[("stun"|"turn"|"turns") ":"][login ":" password "@"]hostname[":" port]["?transport=" ("udp"|"tcp"|"tls")]`. The default scheme is STUN, the default port is 3478 (5349 over TLS), and the default transport is UDP.  For instance, a STUN server URI could be `mystunserver.org`, and a TURN server URI could be `turn:myuser:[email protected]`. Note transports TCP and TLS are only available for a TURN server with libnice as ICE backend and govern only the TURN control connection, meaning relaying is always performed over UDP.
+
+#### rtcDeletePeerConnection
+
+```
+int rtcDeletePeerConnection(int pc)
+```
+
+Deletes the specified Peer Connection.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+
+Return value: `RTC_ERR_SUCCESS` or a negative error code
+
+After this function has been called, `pc` must not be used in a function call anymore. This function will block until all scheduled callbacks of `pc` return (except the one this function might be called in) and no other callback will be called for `pc` after it returns.
+
+#### rtcSetXCallback
+
+These functions set, change, or unset (if `cb` is `NULL`) the different callbacks of a Peer Connection.
+
+```
+int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb)*
+```
+
+`cb` must have the following signature: `void myDescriptionCallback(int pc, const char *sdp, const char *type, void *user_ptr)`
+
+```
+int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myCandidateCallback(int pc, const char *cand, const char *mid, void *user_ptr)`
+
+```
+int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myStateChangeCallback(int pc, rtcState state, void *user_ptr)`
+
+`state` will be one of the following: `RTC_CONNECTING`, `RTC_CONNECTED`, `RTC_DISCONNECTED`, `RTC_FAILED`, or `RTC_CLOSED`.
+
+```
+int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb)
+```
+
+`void myGatheringStateCallback(int pc, rtcGatheringState state, void *user_ptr)`
+
+`state` will be `RTC_GATHERING_INPROGRESS` or `RTC_GATHERING_COMPLETE`.
+
+```
+int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myDataChannelCallback(int pc, int dc, void *user_ptr)`
+
+```
+int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myTrackCallback(int pc, int tr, void *user_ptr)`
+
+#### rtcSetLocalDescription
+
+```
+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.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `type` (optional): type of the description ("offer", "answer", "pranswer", or "rollback") or NULL for autodetection.
+
+#### rtcSetRemoteDescription
+
+```
+int rtcSetRemoteDescription(int pc, const char *sdp, const char *type)
+```
+
+Sets the remote description received from the remote peer by the user's method of choice. The remote description may have candidates or not.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `type` (optional): type of the description ("offer", "answer", "pranswer", or "rollback") or NULL for autodetection.
+
+If the remote description is an offer and `disableAutoNegotiation` was not set in `rtcConfiguration`, the library will automatically answer by calling `rtcSetLocalDescription` internally. Otherwise, the user must call it to answer the remote description.
+
+#### rtcAddRemoteCandidate
+
+```
+int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid)
+```
+
+Adds a trickled remote candidate received from the remote peer by the user's method of choice.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `cand`: a null-terminated SDP string representing the candidate (with or without the `"a="` prefix)
+- `mid` (optional): a null-terminated string representing the mid of the candidate in the remote SDP description or NULL for autodetection
+
+The Peer Connection must have a remote description set.
+
+Return value: `RTC_ERR_SUCCESS` or a negative error code
+
+#### rtcGetLocalDescription
+
+```
+int rtcGetLocalDescription(int pc, char *buffer, int size)
+```
+
+Retrieves the current local description in SDP format.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the description
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the description is not copied but the size is still returned.
+
+#### rtcGetRemoteDescription
+
+```
+int rtcGetRemoteDescription(int pc, char *buffer, int size)
+```
+
+Retrieves the current remote description in SDP format.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the description
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the description is not copied but the size is still returned.
+
+#### rtcGetLocalDescriptionType
+
+```
+int rtcGetLocalDescriptionType(int pc, char *buffer, int size)
+```
+
+Retrieves the current local description type as string.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the type
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the description is not copied but the size is still returned.
+
+#### rtcGetRemoteDescription
+
+```
+int rtcGetRemoteDescriptionType(int pc, char *buffer, int size)
+```
+
+Retrieves the current remote description type as string.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the type
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the description is not copied but the size is still returned.
+
+
+#### rtcGetLocalAddress
+
+```
+int rtcGetLocalAddress(int pc, char *buffer, int size)
+```
+
+Retrieves the current local address, i.e. the network address of the currently selected local candidate. The address will have the format `"IP_ADDRESS:PORT"`, where `IP_ADDRESS` may be either IPv4 or IPv6. The call might fail if the PeerConnection is not in state `RTC_CONNECTED`, and the address might change if the state is not `RTC_COMPLETED`.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the address
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the address is not copied but the size is still returned.
+
+#### rtcGetRemoteAddress
+
+```
+int rtcGetRemoteAddress(int pc, char *buffer, int size)
+```
+
+Retrieves the current remote address, i.e. the network address of the currently selected remote candidate. The address will have the format `"IP_ADDRESS:PORT"`, where `IP_ADDRESS` may be either IPv4 or IPv6. The call may fail if the state is not `RTC_CONNECTED`, and the address might change if the state is not `RTC_COMPLETED`.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `buffer`: a user-supplied buffer to store the address
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the address is not copied but the size is still returned.
+
+#### rtcGetSelectedCandidatePair
+
+```
+int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, int remoteSize)
+```
+
+Retrieve the currently selected candidate pair. The call may fail if the state is not `RTC_CONNECTED`, and the selected candidate pair might change if the state is not `RTC_COMPLETED`.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `local`: a user-supplied buffer to store the local candidate
+- `localSize`: the size of `local`
+- `remote`: a user-supplied buffer to store the remote candidate
+- `remoteSize`: the size of `remote`
+
+Return value: the maximun length of strings copied in buffers (including the terminating null character) or a negative error code
+
+If `local`, `remote`, or both, are `NULL`, the corresponding candidate is not copied, but the maximum length is still returned.
+
+### Data Channel
+
+#### rtcAddDataChannel
+
+```
+int rtcCreateDataChannel(int pc, const char *label)
+int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init)
+
+typedef struct {
+	bool unordered;
+	bool unreliable;
+	unsigned int maxPacketLifeTime;
+	unsigned int maxRetransmits;
+} rtcReliability;
+
+typedef struct {
+	rtcReliability reliability;
+	const char *protocol;
+	bool negotiated;
+	bool manualStream;
+	uint16_t stream;
+} rtcDataChannelInit;
+```
+
+Adds a Data Channel on a Peer Connection. The Peer Connection does not need to be connected, however, the Data Channel will be open only when the Peer Connection is connected.
+
+Arguments:
+- `pc`: identifier of the PeerConnection on which to add a Data Channel
+- `label`: a user-defined UTF-8 string representing the Data Channel name
+- `init`: a structure of initialization settings containing:
+  - `reliability`: a structure of reliability settings containing:
+    - `bool unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
+    - `bool unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
+    - `unsigned int maxPacketLifeTime`: if unreliable, maximum packet life time in milliseconds
+    - `unsigned int maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of retransmissions (0 means no retransmission)
+  - `protocol` (optional): a user-defined UTF-8 string representing the Data Channel protocol, empty if NULL
+  - `negotiated`: if `true`, the Data Channel is assumed to be negotiated by the user and won't be negotiated by the WebRTC layer
+  - `manualStream`: if `true`, the Data Channel will use `stream` as stream ID, else an available id is automatically selected
+  - `stream` (0-65534): if `manualStream` is `true`, the Data Channel will use it as stream ID, else it is ignored
+
+`rtcDataChannel()` is equivalent to `rtcDataChannelEx()` with settings set to ordered, reliable, non-negotiated, with automatic stream ID selection (all flags set to `false`), and `protocol` set to an empty string.
+
+Return value: the identifier of the new Data Channel or a negative error code.
+
+The Data Channel must be deleted with `rtcDeleteDataChannel`.
+
+If `disableAutoNegotiation` was not set in `rtcConfiguration`, the library will automatically initiate the negotiation by calling `rtcSetLocalDescription` internally. Otherwise, the user must call `rtcSetLocalDescription` to initiate the negotiation after creating the first Data Channel.
+
+#### rtcDeleteDataChannel
+
+```
+int rtcDeleteDataChannel(int dc)
+```
+
+Deletes a Data Channel.
+
+Arguments:
+- `dc`: the Data Channel identifier
+
+After this function has been called, `dc` must not be used in a function call anymore. This function will block until all scheduled callbacks of `dc` return (except the one this function might be called in) and no other callback will be called for `dc` after it returns.
+
+#### rtcGetDataChannelStream
+
+```
+int rtcGetDataChannelStream(int dc)
+```
+
+Retrieves the stream ID of the Data Channel.
+
+Arguments:
+- `dc`: the Data Channel identifier
+
+Return value: the stream ID (0-65534) or a negative error code
+
+#### rtcGetDataChannelLabel
+
+```
+int rtcGetDataChannelLabel(int dc, char *buffer, int size)
+```
+
+Retrieves the label of a Data Channel.
+
+Arguments:
+- `dc`: the Data Channel identifier
+- `buffer`: a user-supplied buffer to store the label
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the label is not copied but the size is still returned.
+
+#### rtcGetDataChannelProtocol
+
+```
+int rtcGetDataChannelProtocol(int dc, char *buffer, int size)
+```
+
+Retrieves the protocol of a Data Channel.
+
+Arguments:
+- `dc`: the Data Channel identifier
+- `buffer`: a user-supplied buffer to store the protocol
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the protocol is not copied but the size is still returned.
+
+#### rtcGetDataChannelReliability
+
+```
+int rtcGetDataChannelReliability(int dc, rtcReliability *reliability)
+```
+
+Retrieves the reliability settings of a Data Channel. The function may be called irrelevant of how the Data Channel was created.
+
+Arguments:
+- `dc`: the Data Channel identifier
+- `reliability` a user-supplied structure to fill
+
+Return value: `RTC_ERR_SUCCESS` or a negative error code
+
+### Track
+
+#### rtcAddTrack
+
+```
+int rtcAddTrack(int pc, const char *mediaDescriptionSdp)
+```
+
+Adds a new Track on a Peer Connection. The Peer Connection does not need to be connected, however, the Track will be open only when the Peer Connection is connected.
+
+Arguments:
+- `pc`: the Peer Connection identifier
+- `mediaDescriptionSdp`: a null-terminated string specifying the corresponding media SDP. It must start with a m-line and include a mid parameter.
+
+Return value: the identifier of the new Track or a negative error code
+
+The new track must be deleted with `rtcDeleteTrack`.
+
+The user must call `rtcSetLocalDescription` to negotiate the track.
+
+#### rtcDeleteTrack
+
+```
+int rtcDeleteTrack(int tr)
+```
+
+Deletes a Track.
+
+Arguments:
+- `tr`: the Track identifier
+
+After this function has been called, `tr` must not be used in a function call anymore. This function will block until all scheduled callbacks of `tr` return (except the one this function might be called in) and no other callback will be called for `tr` after it returns.
+
+#### rtcGetTrackDescription
+
+```
+int rtcGetTrackDescription(int tr, char *buffer, int size)
+```
+
+Retrieves the SDP media description of a Track.
+
+Arguments:
+- `dc`: the Track identifier
+- `buffer`: a user-supplied buffer to store the description
+- `size`: the size of `buffer`
+
+Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code
+
+If `buffer` is `NULL`, the description is not copied but the size is still returned.
+
+### WebSocket
+
+#### rtcCreateWebSocket
+
+```
+int rtcCreateWebSocket(const char *url)
+int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config)
+```
+
+Creates a new client WebSocket.
+
+Arguments:
+- `url`: a null-terminated string representing the fully-qualified URL to open.
+- `config`: a structure with the following parameters:
+  - `bool disableTlsVerification`: if true, don't verify the TLS certificate, else try to verify it if possible
+
+Return value: the identifier of the new WebSocket or a negative error code
+
+The new WebSocket must be deleted with `rtcDeleteWebSocket`. The scheme of the URL must be either `ws` or `wss`.
+
+#### rtcDeleteWebSocket
+
+```
+int rtcDeleteWebSocket(int ws)
+```
+
+Arguments:
+- `ws`: the identifier of the WebSocket to delete
+
+Return value: the identifier of the new WebSocket or a negative error code
+
+After this function has been called, `ws` must not be used in a function call anymore. This function will block until all scheduled callbacks of `ws` return (except the one this function might be called in) and no other callback will be called for `ws` after it returns.
+
+### Channel (Data Channel, Track, and WebSocket)
+
+The following common functions might be called with a generic channel identifier. It may be the identifier of either a Data Channel, a Track, or a WebSocket.
+
+#### rtcSetXCallback
+
+These functions set, change, or unset (if `cb` is `NULL`) the different callbacks of a channel.
+
+```
+int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myOpenCallback(int id, void *user_ptr)`
+
+```
+int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myClosedCallback(int id, void *user_ptr)`
+
+```
+int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myErrorCallback(int id, const char *error, void *user_ptr)`
+
+```
+int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myMessageCallback(int id, const char *message, int size, void *user_ptr)`
+
+```
+int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myBufferedAmountLowCallback(int id, void *user_ptr)`
+
+```
+int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb)
+```
+
+`cb` must have the following signature: `void myAvailableCallback(int id, void *user_ptr)`
+
+#### rtcSendMessage
+
+```
+int rtcSendMessage(int id, const char *data, int size)
+```
+
+Arguments:
+- `id`: the channel identifier
+- `data`: the message data
+- `size`: if size >= 0, `data` is interpreted as a binary message of length `size`, otherwise it is interpreted as a null-terminated UTF-8 string.
+
+Return value: `RTC_ERR_SUCCESS` or a negative error code
+
+Sends a message immediately if possible.
+
+Data Channel and WebSocket: If the message may not be sent immediately due to flow control or congestion control, it is buffered until it can actually be sent. You can retrieve the current buffered data size with `rtcGetBufferedAmount`.
+Tracks are an exception: There is no flow or congestion control, messages are never buffered and `rtcGetBufferedAmount` always returns 0.
+
+#### rtcGetBufferedAmount
+
+```
+int rtcGetBufferedAmount(int id)
+```
+
+Retrieves the current buffered amount, i.e. the total size of currently buffered messages waiting to be actually sent in the channel. This does not account for the data buffered at the transport level.
+
+Return value: the buffered amount or a negative error code
+
+#### rtcGetBufferedAmountLowThreshold
+
+```
+int rtcSetBufferedAmountLowThreshold(int id, int amount)
+```
+
+Changes the buffered amount threshold under which `BufferedAmountLowCallback` is called. The callback is called when the buffered amount was strictly superior and gets equal to or lower than the threshold when a message is sent. The initial threshold is 0, meaning the the callback is called each time the buffered amount goes back to zero after being non-zero.
+
+Arguments:
+- `id`: the channel identifier
+- `amount`: the new buffer level threshold
+
+Return value: the identifier of the new WebSocket or a negative error code
+
+#### rtcReceiveMessage
+
+```
+int rtcReceiveMessage(int id, char *buffer, int *size)
+```
+
+Receives a pending message if possible. The function may only be called if `MessageCallback` is not set.
+
+Arguments:
+- `id`: the channel identifier
+- `buffer`: a user-supplied buffer where to write the message data
+- `size`: a pointer to a user-supplied int which must be initialized to the size of `buffer`. On success, the function will write the size of the message to it before returning.
+
+Return value: `RTC_ERR_SUCCESS` or a negative error code (In particular, `RTC_ERR_NOT_AVAIL` is returned when there are no pending messages)
+
+If `buffer` is `NULL`, the message is not copied and kept pending but the size is still written to `size`.
+
+#### rtcGetAvailableAmount
+
+```
+int rtcGetAvailableAmount(int id)
+```
+
+Retrieves the available amount, i.e. the total size of messages pending reception with `rtcReceiveMessage`. The function may only be called if `MessageCallback` is not set.
+
+Arguments:
+- `id`: the channel identifier
+
+Return value: the available amount or a negative error code
+
+

+ 22 - 7
Makefile

@@ -10,6 +10,7 @@ LDFLAGS=-pthread
 LIBS=
 LIBS=
 LOCALLIBS=libusrsctp.a
 LOCALLIBS=libusrsctp.a
 USRSCTP_DIR=deps/usrsctp
 USRSCTP_DIR=deps/usrsctp
+SRTP_DIR=deps/libsrtp
 JUICE_DIR=deps/libjuice
 JUICE_DIR=deps/libjuice
 PLOG_DIR=deps/plog
 PLOG_DIR=deps/plog
 
 
@@ -38,15 +39,22 @@ ifneq ($(USE_GNUTLS), 0)
 endif
 endif
 endif
 endif
 
 
-USE_SRTP ?= 0
-ifneq ($(USE_SRTP), 0)
+NO_MEDIA ?= 0
+USE_SYSTEM_SRTP ?= 0
+ifeq ($(NO_MEDIA), 0)
         CPPFLAGS+=-DRTC_ENABLE_MEDIA=1
         CPPFLAGS+=-DRTC_ENABLE_MEDIA=1
+ifneq ($(USE_SYSTEM_SRTP), 0)
+        CPPFLAGS+=-DRTC_SYSTEM_SRTP=1
         LIBS+=srtp
         LIBS+=srtp
+else
+        CPPFLAGS+=-DRTC_SYSTEM_SRTP=0
+		INCLUDES+=-I$(SRTP_DIR)/include
+        LOCALLIBS+=libsrtp2.a
+endif
 else
 else
         CPPFLAGS+=-DRTC_ENABLE_MEDIA=0
         CPPFLAGS+=-DRTC_ENABLE_MEDIA=0
 endif
 endif
 
 
-
 NO_WEBSOCKET ?= 0
 NO_WEBSOCKET ?= 0
 ifeq ($(NO_WEBSOCKET), 0)
 ifeq ($(NO_WEBSOCKET), 0)
         CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=1
         CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=1
@@ -54,8 +62,8 @@ else
         CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=0
         CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=0
 endif
 endif
 
 
-INCLUDES+=$(shell pkg-config --cflags $(LIBS))
-LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS))
+INCLUDES+=$(if $(LIBS),$(shell pkg-config --cflags $(LIBS)),)
+LDLIBS+=$(LOCALLIBS) $(if $(LIBS),$(shell pkg-config --libs $(LIBS)),)
 
 
 SRCS=$(shell printf "%s " src/*.cpp)
 SRCS=$(shell printf "%s " src/*.cpp)
 OBJS=$(subst .cpp,.o,$(SRCS))
 OBJS=$(subst .cpp,.o,$(SRCS))
@@ -73,7 +81,7 @@ test/%.o: test/%.cpp
 
 
 -include $(subst .cpp,.d,$(SRCS))
 -include $(subst .cpp,.d,$(SRCS))
 
 
-$(NAME).a: $(OBJS)
+$(NAME).a: $(LOCALLIBS) $(OBJS)
 	$(AR) crf $@ $(OBJS)
 	$(AR) crf $@ $(OBJS)
 
 
 $(NAME).so: $(LOCALLIBS) $(OBJS)
 $(NAME).so: $(LOCALLIBS) $(OBJS)
@@ -97,15 +105,22 @@ dist-clean: clean
 	-$(RM) src/*~
 	-$(RM) src/*~
 	-$(RM) test/*~
 	-$(RM) test/*~
 	-cd $(USRSCTP_DIR) && make clean
 	-cd $(USRSCTP_DIR) && make clean
+	-cd $(SRTP_DIR) && make clean
 	-cd $(JUICE_DIR) && make clean
 	-cd $(JUICE_DIR) && make clean
 
 
 libusrsctp.a:
 libusrsctp.a:
 	cd $(USRSCTP_DIR) && \
 	cd $(USRSCTP_DIR) && \
 		./bootstrap && \
 		./bootstrap && \
-		./configure --enable-static --disable-debug CFLAGS="$(CPPFLAGS) -Wno-error=format-truncation" && \
+		./configure --enable-static --disable-debug CFLAGS="-fPIC" && \
 		make
 		make
 	cp $(USRSCTP_DIR)/usrsctplib/.libs/libusrsctp.a .
 	cp $(USRSCTP_DIR)/usrsctplib/.libs/libusrsctp.a .
 
 
+libsrtp2.a:
+	cd $(SRTP_DIR) && \
+		./configure && \
+		make
+	cp $(SRTP_DIR)/libsrtp2.a .
+
 libjuice.a:
 libjuice.a:
 ifneq ($(USE_GNUTLS), 0)
 ifneq ($(USE_GNUTLS), 0)
 	cd $(JUICE_DIR) && make USE_NETTLE=1
 	cd $(JUICE_DIR) && make USE_NETTLE=1

+ 26 - 92
README.md

@@ -5,13 +5,11 @@ 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/).
 - The connectivity for WebRTC can be provided through my ad-hoc ICE library [libjuice](https://github.com/paullouisageneau/libjuice) as submodule or through [libnice](https://github.com/libnice/libnice).
 - The connectivity for WebRTC can be provided through my ad-hoc ICE library [libjuice](https://github.com/paullouisageneau/libjuice) as submodule or through [libnice](https://github.com/libnice/libnice).
 
 
-This projet is originally inspired by [librtcdcpp](https://github.com/chadnickbok/librtcdcpp), however it is a complete rewrite from scratch, because the messy architecture of librtcdcpp made solving its implementation issues difficult.
-
 Licensed under LGPLv2, see [LICENSE](https://github.com/paullouisageneau/libdatachannel/blob/master/LICENSE).
 Licensed under LGPLv2, see [LICENSE](https://github.com/paullouisageneau/libdatachannel/blob/master/LICENSE).
 
 
 ## Compatibility
 ## Compatibility
 
 
-The library aims at implementing the following communication protocols:
+The library implements the following communication protocols:
 
 
 ### WebRTC Data Channels and Media Transport
 ### WebRTC Data Channels and Media Transport
 
 
@@ -47,89 +45,24 @@ Features:
 
 
 ## Dependencies
 ## Dependencies
 
 
-Dependencies:
-- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/
+Only [GnuTLS](https://www.gnutls.org/) or [OpenSSL](https://www.openssl.org/) are necessary.
+
+Optionally, [libnice](https://nice.freedesktop.org/) can be selected as an alternative ICE backend instead of libjuice.
 
 
 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
 
 
-Optional dependencies:
-- libnice: https://nice.freedesktop.org/ (if selected as ICE backend instead of libjuice)
-- libsrtp: https://github.com/cisco/libsrtp (if selected instead of the submodule)
-
 ## Building
 ## Building
 
 
-### Clone repository and submodules
-
-```bash
-$ git clone https://github.com/paullouisageneau/libdatachannel.git
-$ cd libdatachannel
-$ git submodule update --init --recursive
-```
-
-### Building with CMake
-
-The CMake library targets `libdatachannel` and `libdatachannel-static` respectively correspond to the shared and static libraries. The default target will build tests and examples. The option `USE_GNUTLS` allows to switch between OpenSSL (default) and GnuTLS, and the option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice.
-
-#### POSIX-compliant operating systems (including Linux and Apple macOS)
-```bash
-$ cmake -B build -DUSE_GNUTLS=1 -DUSE_NICE=0
-$ cd build
-$ make -j2
-```
-
-#### Apple macOS with XCode project
-
-```bash
-$ cmake -B "$BUILD_DIR" -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode
-```
-
-Xcode project is generated in *build/* directory.
-
-##### Solving **Could NOT find OpenSSL** error
-
-You need to add OpenSSL root directory if your build fails with the following message:
-
-```
-Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the
-system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY
-OPENSSL_INCLUDE_DIR)
-```
-
-for example:
-```bash
-$ cmake -B build -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl\@1.1/1.1.1h/
-```
-
-#### Microsoft Windows with MinGW cross-compilation
-```bash
-$ cmake -B build -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
-$ cd build
-$ make -j2
-```
-
-#### Microsoft Windows with Microsoft Visual C++
-```bash
-$ cmake -B build -G "NMake Makefiles"
-$ cd build
-$ nmake
-```
-
-### Building directly with Make (Linux only)
-
-The option `USE_GNUTLS` allows to switch between OpenSSL (default) and GnuTLS, and the option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice.
-
-```bash
-$ make USE_GNUTLS=1 USE_NICE=0
-```
+See [BUILDING.md](https://github.com/paullouisageneau/libdatachannel/blob/master/BUILDING.md) for building instructions.
 
 
 ## Examples
 ## Examples
 
 
-See [examples](https://github.com/paullouisageneau/libdatachannel/blob/master/examples/) for a complete usage example with signaling server (under GPLv2).
+See [examples](https://github.com/paullouisageneau/libdatachannel/blob/master/examples/) for complete usage examples with signaling server (under GPLv2).
 
 
-Additionnaly, you might want to have a look at the [C API](https://github.com/paullouisageneau/libdatachannel/blob/master/include/rtc/rtc.h).
+Additionnaly, you might want to have a look at the [C API documentation](https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md).
 
 
 ### Signal a PeerConnection
 ### Signal a PeerConnection
 
 
@@ -141,44 +74,43 @@ Additionnaly, you might want to have a look at the [C API](https://github.com/pa
 rtc::Configuration config;
 rtc::Configuration config;
 config.iceServers.emplace_back("mystunserver.org:3478");
 config.iceServers.emplace_back("mystunserver.org:3478");
 
 
-auto pc = make_shared<rtc::PeerConnection>(config);
+rtc::PeerConection pc(config);
 
 
-pc->onLocalDescription([](rtc::Description sdp) {
+pc.onLocalDescription([](rtc::Description sdp) {
     // Send the SDP to the remote peer
     // Send the SDP to the remote peer
     MY_SEND_DESCRIPTION_TO_REMOTE(string(sdp));
     MY_SEND_DESCRIPTION_TO_REMOTE(string(sdp));
 });
 });
 
 
-pc->onLocalCandidate([](rtc::Candidate candidate) {
+pc.onLocalCandidate([](rtc::Candidate candidate) {
     // Send the candidate to the remote peer
     // Send the candidate to the remote peer
     MY_SEND_CANDIDATE_TO_REMOTE(candidate.candidate(), candidate.mid());
     MY_SEND_CANDIDATE_TO_REMOTE(candidate.candidate(), candidate.mid());
 });
 });
 
 
-MY_ON_RECV_DESCRIPTION_FROM_REMOTE([pc](string sdp) {
-    pc->setRemoteDescription(rtc::Description(sdp));
+MY_ON_RECV_DESCRIPTION_FROM_REMOTE([&pc](string sdp) {
+    pc.setRemoteDescription(rtc::Description(sdp));
 });
 });
 
 
-MY_ON_RECV_CANDIDATE_FROM_REMOTE([pc](string candidate, string mid) {
-    pc->addRemoteCandidate(rtc::Candidate(candidate, mid));
+MY_ON_RECV_CANDIDATE_FROM_REMOTE([&pc](string candidate, string mid) {
+    pc.addRemoteCandidate(rtc::Candidate(candidate, mid));
 });
 });
 ```
 ```
 
 
 ### Observe the PeerConnection state
 ### Observe the PeerConnection state
 
 
 ```cpp
 ```cpp
-pc->onStateChange([](PeerConnection::State state) {
+pc.onStateChange([](PeerConnection::State state) {
     cout << "State: " << state << endl;
     cout << "State: " << state << endl;
 });
 });
 
 
-pc->onGatheringStateChange([](PeerConnection::GatheringState state) {
+pc.onGatheringStateChange([](PeerConnection::GatheringState state) {
     cout << "Gathering state: " << state << endl;
     cout << "Gathering state: " << state << endl;
 });
 });
-
 ```
 ```
 
 
 ### Create a DataChannel
 ### Create a DataChannel
 
 
 ```cpp
 ```cpp
-auto dc = pc->createDataChannel("test");
+auto dc = pc.createDataChannel("test");
 
 
 dc->onOpen([]() {
 dc->onOpen([]() {
     cout << "Open" << endl;
     cout << "Open" << endl;
@@ -195,30 +127,28 @@ dc->onMessage([](variant<binary, string> message) {
 
 
 ```cpp
 ```cpp
 shared_ptr<rtc::DataChannel> dc;
 shared_ptr<rtc::DataChannel> dc;
-pc->onDataChannel([&dc](shared_ptr<rtc::DataChannel> incoming) {
+pc.onDataChannel([&dc](shared_ptr<rtc::DataChannel> incoming) {
     dc = incoming;
     dc = incoming;
     dc->send("Hello world!");
     dc->send("Hello world!");
 });
 });
-
 ```
 ```
 
 
 ### Open a WebSocket
 ### Open a WebSocket
 
 
 ```cpp
 ```cpp
-auto ws = make_shared<rtc::WebSocket>();
+rtc::WebSocket ws;
 
 
-ws->onOpen([]() {
+ws.onOpen([]() {
 	cout << "WebSocket open" << endl;
 	cout << "WebSocket open" << endl;
 });
 });
 
 
-ws->onMessage([](variant<binary, string> message) {
+ws.onMessage([](variant<binary, string> message) {
     if (holds_alternative<string>(message)) {
     if (holds_alternative<string>(message)) {
         cout << "WebSocket received: " << get<string>(message) << endl;
         cout << "WebSocket received: " << get<string>(message) << endl;
     }
     }
 });
 });
 
 
-ws->open("wss://my.websocket/service");
-
+ws.open("wss://my.websocket/service");
 ```
 ```
 
 
 ## External resources
 ## External resources
@@ -227,3 +157,7 @@ ws->open("wss://my.websocket/service");
 - Unity wrapper for Windows 10 and Hololens: [datachannel-unity](https://github.com/hanseuljun/datachannel-unity)
 - Unity wrapper for Windows 10 and Hololens: [datachannel-unity](https://github.com/hanseuljun/datachannel-unity)
 - WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm)
 - WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm)
 
 
+## Thanks
+
+Thanks to [Streamr](https://streamr.network/) for sponsoring this work!
+

+ 1 - 1
deps/libjuice

@@ -1 +1 @@
-Subproject commit 00841f2fa0f3aa3ec25e3e6f1da4f8086d132f84
+Subproject commit e68ef6a6e3b74602dfa4166447d9174c3e4518ee

+ 1 - 1
deps/usrsctp

@@ -1 +1 @@
-Subproject commit 2e754d58227f76b8d8c7358ee5f5770b78cc239a
+Subproject commit 07f871bda23943c43c9e74cc54f25130459de830

+ 4 - 3
examples/README.md

@@ -1,9 +1,10 @@
-# Examples for libdatachannel
+# libdatachannel - Examples
 
 
 This directory contains different WebRTC clients and compatible WebSocket + JSON signaling servers.
 This directory contains different WebRTC clients and compatible WebSocket + JSON signaling servers.
 
 
-- [client](client) uses libdatachannel to implement WebRTC Data Channels with WebSocket signaling
-- [web](web) contains an equivalent implementation for web browsers and a node.js signaling server
+- [client](client) contains a native client to open Data Channels with WebSocket signaling using libdatachannel
+- [web](web) contains a equivalent JavaScript client for web browsers
+- [signaling-server-nodejs](signaling-server-nodejs) contains a signaling server in node.js
 - [signaling-server-python](signaling-server-python) contains a similar signaling server in Python
 - [signaling-server-python](signaling-server-python) contains a similar signaling server in Python
 - [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)
 
 

+ 10 - 3
examples/client/CMakeLists.txt

@@ -4,10 +4,10 @@ if(POLICY CMP0079)
 endif()
 endif()
 
 
 if(WIN32)
 if(WIN32)
-add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h getopt.cpp getopt.h)
-target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
+	add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h getopt.cpp getopt.h)
+	target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
 else()
 else()
-add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
+	add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
 endif()
 endif()
 
 
 set_target_properties(datachannel-client PROPERTIES
 set_target_properties(datachannel-client PROPERTIES
@@ -15,3 +15,10 @@ set_target_properties(datachannel-client PROPERTIES
 	OUTPUT_NAME client)
 	OUTPUT_NAME client)
 target_link_libraries(datachannel-client datachannel nlohmann_json)
 target_link_libraries(datachannel-client datachannel nlohmann_json)
 
 
+if(WIN32)
+	add_custom_command(TARGET datachannel-client POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-client>
+	)
+endif()

+ 8 - 7
examples/client/main.cpp

@@ -54,20 +54,20 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
 string randomId(size_t length);
 string randomId(size_t length);
 
 
 int main(int argc, char **argv) try {
 int main(int argc, char **argv) try {
-	auto params = std::make_unique<Cmdline>(argc, argv);
+	Cmdline params(argc, argv);
 
 
 	rtc::InitLogger(LogLevel::Info);
 	rtc::InitLogger(LogLevel::Info);
 
 
 	Configuration config;
 	Configuration config;
 	string stunServer = "";
 	string stunServer = "";
-	if (params->noStun()) {
+	if (params.noStun()) {
 		cout << "No STUN server is configured. Only local hosts and public IP addresses supported."
 		cout << "No STUN server is configured. Only local hosts and public IP addresses supported."
 		     << endl;
 		     << endl;
 	} else {
 	} else {
-		if (params->stunServer().substr(0, 5).compare("stun:") != 0) {
+		if (params.stunServer().substr(0, 5).compare("stun:") != 0) {
 			stunServer = "stun:";
 			stunServer = "stun:";
 		}
 		}
-		stunServer += params->stunServer() + ":" + to_string(params->stunPort());
+		stunServer += params.stunServer() + ":" + to_string(params.stunPort());
 		cout << "Stun server is " << stunServer << endl;
 		cout << "Stun server is " << stunServer << endl;
 		config.iceServers.emplace_back(stunServer);
 		config.iceServers.emplace_back(stunServer);
 	}
 	}
@@ -129,11 +129,11 @@ int main(int argc, char **argv) try {
 	});
 	});
 
 
 	string wsPrefix = "";
 	string wsPrefix = "";
-	if (params->webSocketServer().substr(0, 5).compare("ws://") != 0) {
+	if (params.webSocketServer().substr(0, 5).compare("ws://") != 0) {
 		wsPrefix = "ws://";
 		wsPrefix = "ws://";
 	}
 	}
-	const string url = wsPrefix + params->webSocketServer() + ":" +
-	                   to_string(params->webSocketPort()) + "/" + localId;
+	const string url = wsPrefix + params.webSocketServer() + ":" +
+	                   to_string(params.webSocketPort()) + "/" + localId;
 	cout << "Url is " << url << endl;
 	cout << "Url is " << url << endl;
 	ws->open(url);
 	ws->open(url);
 
 
@@ -251,3 +251,4 @@ 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;
 }
 }
+

+ 12 - 0
examples/copy-paste-capi/CMakeLists.txt

@@ -13,3 +13,15 @@ set_target_properties(datachannel-copy-paste-capi-answerer PROPERTIES
 	OUTPUT_NAME answerer)
 	OUTPUT_NAME answerer)
 target_link_libraries(datachannel-copy-paste-capi-answerer datachannel)
 target_link_libraries(datachannel-copy-paste-capi-answerer datachannel)
 
 
+if(WIN32)
+	add_custom_command(TARGET datachannel-copy-paste-capi-offerer POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-copy-paste-capi-offerer>
+	)
+	add_custom_command(TARGET datachannel-copy-paste-capi-answerer POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-copy-paste-capi-answerer>
+	)
+endif()

+ 12 - 0
examples/copy-paste/CMakeLists.txt

@@ -12,3 +12,15 @@ set_target_properties(datachannel-copy-paste-answerer PROPERTIES
 	OUTPUT_NAME answerer)
 	OUTPUT_NAME answerer)
 target_link_libraries(datachannel-copy-paste-answerer datachannel)
 target_link_libraries(datachannel-copy-paste-answerer datachannel)
 
 
+if(WIN32)
+	add_custom_command(TARGET datachannel-copy-paste-offerer POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-copy-paste-offerer>
+	)
+	add_custom_command(TARGET datachannel-copy-paste-answerer POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-copy-paste-answerer>
+	)
+endif()

+ 7 - 0
examples/media/CMakeLists.txt

@@ -6,3 +6,10 @@ set_target_properties(datachannel-media PROPERTIES
         OUTPUT_NAME media)
         OUTPUT_NAME media)
 target_link_libraries(datachannel-media datachannel nlohmann_json)
 target_link_libraries(datachannel-media datachannel nlohmann_json)
 
 
+if(WIN32)
+	add_custom_command(TARGET datachannel-media POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-media>
+	)
+endif()

+ 1 - 1
examples/media/main.cpp

@@ -68,7 +68,7 @@ int main() {
 		auto track = pc->addTrack(media);
 		auto track = pc->addTrack(media);
 
 
 		auto session = std::make_shared<rtc::RtcpReceivingSession>();
 		auto session = std::make_shared<rtc::RtcpReceivingSession>();
-		track->setRtcpHandler(session);
+		track->setMediaHandler(session);
 
 
 		track->onMessage(
 		track->onMessage(
 		    [session, sock, addr](rtc::binary message) {
 		    [session, sock, addr](rtc::binary message) {

+ 7 - 0
examples/sfu-media/CMakeLists.txt

@@ -6,3 +6,10 @@ set_target_properties(datachannel-sfu-media PROPERTIES
 		OUTPUT_NAME sfu-media)
 		OUTPUT_NAME sfu-media)
 target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)
 target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)
 
 
+if(WIN32)
+	add_custom_command(TARGET datachannel-sfu-media POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:datachannel-sfu-media>
+	)
+endif()

+ 1 - 1
examples/sfu-media/main.cpp

@@ -60,7 +60,7 @@ int main() {
 		pc->setLocalDescription();
 		pc->setLocalDescription();
 
 
 		auto session = std::make_shared<rtc::RtcpReceivingSession>();
 		auto session = std::make_shared<rtc::RtcpReceivingSession>();
-		track->setRtcpHandler(session);
+		track->setMediaHandler(session);
 
 
 		const rtc::SSRC targetSSRC = 4;
 		const rtc::SSRC targetSSRC = 4;
 
 

+ 339 - 0
examples/signaling-server-nodejs/LICENSE

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 285 - 0
examples/signaling-server-nodejs/package-lock.json

@@ -0,0 +1,285 @@
+{
+  "name": "libdatachannel-signaling-server",
+  "version": "0.1.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "libdatachannel-signaling-server",
+      "version": "0.1.0",
+      "license": "GPL-2.0",
+      "dependencies": {
+        "websocket": "^1.0.33"
+      }
+    },
+    "node_modules/bufferutil": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz",
+      "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "node-gyp-build": "^4.2.0"
+      }
+    },
+    "node_modules/d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "dependencies": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "dependencies": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "node_modules/es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "dependencies": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "node_modules/ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "dependencies": {
+        "type": "^2.0.0"
+      }
+    },
+    "node_modules/ext/node_modules/type": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
+      "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
+    },
+    "node_modules/is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+    },
+    "node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "node_modules/next-tick": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+    },
+    "node_modules/node-gyp-build": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
+      "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
+    "node_modules/type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+    },
+    "node_modules/typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+      "dependencies": {
+        "is-typedarray": "^1.0.0"
+      }
+    },
+    "node_modules/utf-8-validate": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz",
+      "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "node-gyp-build": "^4.2.0"
+      }
+    },
+    "node_modules/websocket": {
+      "version": "1.0.33",
+      "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz",
+      "integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==",
+      "dependencies": {
+        "bufferutil": "^4.0.1",
+        "debug": "^2.2.0",
+        "es5-ext": "^0.10.50",
+        "typedarray-to-buffer": "^3.1.5",
+        "utf-8-validate": "^5.0.2",
+        "yaeti": "^0.0.6"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/yaeti": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
+      "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=",
+      "engines": {
+        "node": ">=0.10.32"
+      }
+    }
+  },
+  "dependencies": {
+    "bufferutil": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz",
+      "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==",
+      "requires": {
+        "node-gyp-build": "^4.2.0"
+      }
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "requires": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "requires": {
+        "type": "^2.0.0"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
+          "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
+        }
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "next-tick": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+    },
+    "node-gyp-build": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
+      "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg=="
+    },
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+    },
+    "typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+      "requires": {
+        "is-typedarray": "^1.0.0"
+      }
+    },
+    "utf-8-validate": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz",
+      "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==",
+      "requires": {
+        "node-gyp-build": "^4.2.0"
+      }
+    },
+    "websocket": {
+      "version": "1.0.33",
+      "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz",
+      "integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==",
+      "requires": {
+        "bufferutil": "^4.0.1",
+        "debug": "^2.2.0",
+        "es5-ext": "^0.10.50",
+        "typedarray-to-buffer": "^3.1.5",
+        "utf-8-validate": "^5.0.2",
+        "yaeti": "^0.0.6"
+      }
+    },
+    "yaeti": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
+      "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
+    }
+  }
+}

+ 4 - 4
examples/web/package.json → examples/signaling-server-nodejs/package.json

@@ -1,10 +1,10 @@
 {
 {
-  "name": "libdatachannel-example-web",
-  "version": "0.0.1",
-  "description": "Example for libdatachannel",
+  "name": "libdatachannel-signaling-server",
+  "version": "0.1.0",
+  "description": "Signaling server example for libdatachannel",
   "main": "server.js",
   "main": "server.js",
   "scripts": {
   "scripts": {
-    "start": "node server.js",
+    "start": "node signaling-server.js",
     "test": "echo \"Error: no test specified\" && exit 1"
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   },
   "repository": {
   "repository": {

+ 10 - 38
examples/web/server.js → examples/signaling-server-nodejs/signaling-server.js

@@ -21,12 +21,6 @@ const fs = require('fs');
 const http = require('http');
 const http = require('http');
 const websocket = require('websocket');
 const websocket = require('websocket');
 
 
-const staticFiles = {
-  '/index.html': 'text/html',
-  '/style.css': 'text/css',
-  '/script.js': 'text/javascript',
-};
-
 const clients = {};
 const clients = {};
 
 
 const httpServer = http.createServer((req, res) => {
 const httpServer = http.createServer((req, res) => {
@@ -34,58 +28,38 @@ const httpServer = http.createServer((req, res) => {
 
 
   const respond = (code, data, contentType = 'text/plain') => {
   const respond = (code, data, contentType = 'text/plain') => {
     res.writeHead(code, {
     res.writeHead(code, {
-      'Content-Type': contentType,
-      'Access-Control-Allow-Origin': '*',
+      'Content-Type' : contentType,
+      'Access-Control-Allow-Origin' : '*',
     });
     });
     res.end(data);
     res.end(data);
   };
   };
 
 
-  if(req.method != 'GET') {
-    respond(405, 'Method not allowed');
-    return;
-  }
-
-  const url = req.url == '/' ? '/index.html' : req.url;
-  const contentType = staticFiles[url];
-  if(!contentType) {
-    respond(404, 'Not found');
-    return;
-  }
-
-  fs.readFile(__dirname + url, (err, data) => {
-    if(err) {
-      respond(500, 'Missing file');
-      return;
-    }
-
-    respond(200, data, contentType);
-  });
+  respond(404, 'Not Found');
 });
 });
 
 
-const wsServer = new websocket.server({ httpServer });
+const wsServer = new websocket.server({httpServer});
 wsServer.on('request', (req) => {
 wsServer.on('request', (req) => {
   console.log(`WS  ${req.resource}`);
   console.log(`WS  ${req.resource}`);
 
 
-  const { path } = req.resourceURL;
+  const {path} = req.resourceURL;
   const splitted = path.split('/');
   const splitted = path.split('/');
   splitted.shift();
   splitted.shift();
   const id = splitted[0];
   const id = splitted[0];
 
 
   const conn = req.accept(null, req.origin);
   const conn = req.accept(null, req.origin);
   conn.on('message', (data) => {
   conn.on('message', (data) => {
-    if(data.type === 'utf8') {
+    if (data.type === 'utf8') {
       console.log(`Client ${id} << ${data.utf8Data}`);
       console.log(`Client ${id} << ${data.utf8Data}`);
 
 
       const message = JSON.parse(data.utf8Data);
       const message = JSON.parse(data.utf8Data);
       const destId = message.id;
       const destId = message.id;
       const dest = clients[destId];
       const dest = clients[destId];
-      if(dest) {
+      if (dest) {
         message.id = id;
         message.id = id;
         const data = JSON.stringify(message);
         const data = JSON.stringify(message);
         console.log(`Client ${destId} >> ${data}`);
         console.log(`Client ${destId} >> ${data}`);
         dest.send(data);
         dest.send(data);
-      }
-      else {
+      } else {
         console.error(`Client ${destId} not found`);
         console.error(`Client ${destId} not found`);
       }
       }
     }
     }
@@ -103,7 +77,5 @@ const splitted = endpoint.split(':');
 const port = splitted.pop();
 const port = splitted.pop();
 const hostname = splitted.join(':') || '127.0.0.1';
 const hostname = splitted.join(':') || '127.0.0.1';
 
 
-httpServer.listen(port, hostname, () => {
-  console.log(`Server listening on ${hostname}:${port}`);
-});
-
+httpServer.listen(port, hostname,
+                  () => { console.log(`Server listening on ${hostname}:${port}`); });

+ 27 - 6
examples/streamer/CMakeLists.txt

@@ -3,17 +3,38 @@ if(POLICY CMP0079)
 	cmake_policy(SET CMP0079 NEW)
 	cmake_policy(SET CMP0079 NEW)
 endif()
 endif()
 
 
+add_executable(streamer
+	main.cpp
+	dispatchqueue.cpp
+	dispatchqueue.hpp
+	h264fileparser.cpp
+	h264fileparser.hpp
+	helpers.cpp
+	helpers.hpp
+	opusfileparser.cpp
+	opusfileparser.hpp
+	fileparser.cpp
+	fileparser.hpp
+	stream.cpp
+	stream.hpp
+	ArgParser.cpp
+	ArgParser.hpp
+)
+
 if(WIN32)
 if(WIN32)
-add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp ArgParser.hpp ArgParser.cpp)
-target_compile_definitions(streamer PUBLIC STATIC_GETOPT)
-else()
-add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp ArgParser.hpp ArgParser.cpp)
+	target_compile_definitions(streamer PUBLIC STATIC_GETOPT)
 endif()
 endif()
+
 set_target_properties(streamer PROPERTIES
 set_target_properties(streamer PROPERTIES
 	CXX_STANDARD 17
 	CXX_STANDARD 17
 	OUTPUT_NAME streamer)
 	OUTPUT_NAME streamer)
 
 
-target_link_libraries(streamer datachannel)
-
 target_link_libraries(streamer datachannel nlohmann_json)
 target_link_libraries(streamer datachannel nlohmann_json)
 
 
+if(WIN32)
+	add_custom_command(TARGET streamer POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy_if_different
+		"$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+		$<TARGET_FILE_DIR:streamer>
+	)
+endif()

+ 4 - 4
examples/streamer/README.md

@@ -2,19 +2,19 @@
 
 
 This example streams H264 and opus<sup id="a1">[1](#f1)</sup> samples to the connected browser client.
 This example streams H264 and opus<sup id="a1">[1](#f1)</sup> samples to the connected browser client.
 
 
-## Starting signaling server
+## Start a signaling server
 
 
 ```sh
 ```sh
 $ python3 ../signaling-server-python/signaling-server.py
 $ python3 ../signaling-server-python/signaling-server.py
 ```
 ```
 
 
-## Starting php
+## Start a web server
 
 
 ```sh
 ```sh
-$ php -S 127.0.0.1:8080
+$ python3 -m http.server --bind 127.0.0.1 8080
 ```
 ```
 
 
-Now you can open demo at [127.0.0.1:8080](127.0.0.1:8080).
+Now you can open the demo at [http://127.0.0.1:8080](http://127.0.0.1:8080).
 
 
 ## Arguments
 ## Arguments
 
 

+ 4 - 6
examples/streamer/client.js

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

+ 1 - 1
examples/streamer/helpers.cpp

@@ -60,7 +60,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz)
 using namespace std;
 using namespace std;
 using namespace rtc;
 using namespace rtc;
 
 
-ClientTrackData::ClientTrackData(shared_ptr<Track> track, shared_ptr<RtcpSenderReporter> sender) {
+ClientTrackData::ClientTrackData(shared_ptr<Track> track, shared_ptr<RtcpSrReporter> sender) {
     this->track = track;
     this->track = track;
     this->sender = sender;
     this->sender = sender;
 }
 }

+ 2 - 2
examples/streamer/helpers.hpp

@@ -23,9 +23,9 @@
 
 
 struct ClientTrackData {
 struct ClientTrackData {
     std::shared_ptr<rtc::Track> track;
     std::shared_ptr<rtc::Track> track;
-    std::shared_ptr<rtc::RtcpSenderReporter> sender;
+	std::shared_ptr<rtc::RtcpSrReporter> sender;
 
 
-    ClientTrackData(std::shared_ptr<rtc::Track> track, std::shared_ptr<rtc::RtcpSenderReporter> sender);
+	ClientTrackData(std::shared_ptr<rtc::Track> track, std::shared_ptr<rtc::RtcpSrReporter> sender);
 };
 };
 
 
 struct Client {
 struct Client {

+ 0 - 1
examples/streamer/index.html

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

+ 26 - 14
examples/streamer/main.cpp

@@ -70,7 +70,7 @@ DispatchQueue MainThread("Main");
 /// Audio and video stream
 /// Audio and video stream
 optional<shared_ptr<Stream>> avStream = nullopt;
 optional<shared_ptr<Stream>> avStream = nullopt;
 
 
-const string defaultRootDirectory = "../../../../examples/streamer/samples/";
+const string defaultRootDirectory = "../../../examples/streamer/samples/";
 const string defaultH264SamplesDirectory = defaultRootDirectory + "h264/";
 const string defaultH264SamplesDirectory = defaultRootDirectory + "h264/";
 string h264SamplesDirectory = defaultH264SamplesDirectory;
 string h264SamplesDirectory = defaultH264SamplesDirectory;
 const string defaultOpusSamplesDirectory = defaultRootDirectory + "opus/";
 const string defaultOpusSamplesDirectory = defaultRootDirectory + "opus/";
@@ -161,12 +161,13 @@ int main(int argc, char **argv) try {
     string stunServer = "stun:stun.l.google.com:19302";
     string stunServer = "stun:stun.l.google.com:19302";
     cout << "Stun server is " << stunServer << endl;
     cout << "Stun server is " << stunServer << endl;
     config.iceServers.emplace_back(stunServer);
     config.iceServers.emplace_back(stunServer);
-
+    config.disableAutoNegotiation = true;
 
 
     string localId = "server";
     string localId = "server";
     cout << "The local ID is: " << localId << endl;
     cout << "The local ID is: " << localId << endl;
 
 
     auto ws = make_shared<WebSocket>();
     auto ws = make_shared<WebSocket>();
+
     ws->onOpen([]() { cout << "WebSocket connected, signaling ready" << endl; });
     ws->onOpen([]() { cout << "WebSocket connected, signaling ready" << endl; });
 
 
     ws->onClosed([]() { cout << "WebSocket closed" << endl; });
     ws->onClosed([]() { cout << "WebSocket closed" << endl; });
@@ -214,36 +215,48 @@ int main(int argc, char **argv) try {
 shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
 shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
     auto video = Description::Video(cname);
     auto video = Description::Video(cname);
     video.addH264Codec(payloadType);
     video.addH264Codec(payloadType);
-    video.addSSRC(ssrc, cname, msid);
+    video.addSSRC(ssrc, cname, msid, cname);
     auto track = pc->addTrack(video);
     auto track = pc->addTrack(video);
     // create RTP configuration
     // create RTP configuration
     auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate));
     auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate));
     // create packetizer
     // create packetizer
-    auto packetizer = shared_ptr<H264RtpPacketizer>(new H264RtpPacketizer(rtpConfig));
-    // create H264 and RTCP SP handler
-    shared_ptr<H264PacketizationHandler> h264Handler(new H264PacketizationHandler(H264PacketizationHandler::Separator::Length, packetizer));
+    auto packetizer = shared_ptr<H264RtpPacketizer>(new H264RtpPacketizer(H264RtpPacketizer::Separator::Length, rtpConfig));
+    // create H264 handler
+    shared_ptr<H264PacketizationHandler> h264Handler(new H264PacketizationHandler(packetizer));
+    // add RTCP SR handler
+    auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
+    h264Handler->addToChain(srReporter);
+    // add RTCP NACK handler
+    auto nackResponder = make_shared<RtcpNackResponder>();
+    h264Handler->addToChain(nackResponder);
     // set handler
     // set handler
-    track->setRtcpHandler(h264Handler);
+    track->setMediaHandler(h264Handler);
     track->onOpen(onOpen);
     track->onOpen(onOpen);
-    auto trackData = make_shared<ClientTrackData>(track, h264Handler);
+    auto trackData = make_shared<ClientTrackData>(track, srReporter);
     return trackData;
     return trackData;
 }
 }
 
 
 shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
 shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const uint8_t payloadType, const uint32_t ssrc, const string cname, const string msid, const function<void (void)> onOpen) {
     auto audio = Description::Audio(cname);
     auto audio = Description::Audio(cname);
     audio.addOpusCodec(payloadType);
     audio.addOpusCodec(payloadType);
-    audio.addSSRC(ssrc, cname, msid);
+    audio.addSSRC(ssrc, cname, msid, cname);
     auto track = pc->addTrack(audio);
     auto track = pc->addTrack(audio);
     // create RTP configuration
     // create RTP configuration
     auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate));
     auto rtpConfig = shared_ptr<RtpPacketizationConfig>(new RtpPacketizationConfig(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate));
     // create packetizer
     // create packetizer
     auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig);
     auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig);
-    // create opus and RTCP SP handler
+    // create opus handler
     auto opusHandler = make_shared<OpusPacketizationHandler>(packetizer);
     auto opusHandler = make_shared<OpusPacketizationHandler>(packetizer);
+    // add RTCP SR handler
+    auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
+    opusHandler->addToChain(srReporter);
+    // add RTCP NACK handler
+    auto nackResponder = make_shared<RtcpNackResponder>();
+    opusHandler->addToChain(nackResponder);
     // set handler
     // set handler
-    track->setRtcpHandler(opusHandler);
+    track->setMediaHandler(opusHandler);
     track->onOpen(onOpen);
     track->onOpen(onOpen);
-    auto trackData = make_shared<ClientTrackData>(track, opusHandler);
+    auto trackData = make_shared<ClientTrackData>(track, srReporter);
     return trackData;
     return trackData;
 }
 }
 
 
@@ -251,7 +264,6 @@ shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const
 shared_ptr<Client> createPeerConnection(const Configuration &config,
 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));
     shared_ptr<Client> client(new Client(pc));
 
 
@@ -304,7 +316,7 @@ shared_ptr<Client> createPeerConnection(const Configuration &config,
         cout << "Audio from " << id << " opened" << endl;
         cout << "Audio from " << id << " opened" << endl;
     });
     });
 
 
-    auto dc = pc->addDataChannel("ping-pong");
+    auto dc = pc->createDataChannel("ping-pong");
     dc->onOpen([id, wdc = make_weak_ptr(dc)]() {
     dc->onOpen([id, wdc = make_weak_ptr(dc)]() {
         if (auto dc = wdc.lock()) {
         if (auto dc = wdc.lock()) {
             dc->send("Ping");
             dc->send("Ping");

+ 9 - 0
examples/web/README.md

@@ -0,0 +1,9 @@
+# libdatachannel - Equivalent JavaScript client
+
+Start a web server in this directory with:
+```sh
+$ python3 -m http.server --bind 127.0.0.1 8080
+```
+
+You can now open the web client at [http://127.0.0.1:8080](http://127.0.0.1:8080).
+

+ 0 - 136
examples/web/package-lock.json

@@ -1,136 +0,0 @@
-{
-  "name": "libdatachannel-example-web",
-  "version": "0.0.1",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "bufferutil": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz",
-      "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==",
-      "requires": {
-        "node-gyp-build": "^4.2.0"
-      }
-    },
-    "d": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-      "requires": {
-        "es5-ext": "^0.10.50",
-        "type": "^1.0.1"
-      }
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
-    "es5-ext": {
-      "version": "0.10.53",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
-      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
-      "requires": {
-        "es6-iterator": "~2.0.3",
-        "es6-symbol": "~3.1.3",
-        "next-tick": "~1.0.0"
-      }
-    },
-    "es6-iterator": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
-      "requires": {
-        "d": "1",
-        "es5-ext": "^0.10.35",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "es6-symbol": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
-      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
-      "requires": {
-        "d": "^1.0.1",
-        "ext": "^1.1.2"
-      }
-    },
-    "ext": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
-      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
-      "requires": {
-        "type": "^2.0.0"
-      },
-      "dependencies": {
-        "type": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
-          "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
-        }
-      }
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
-    },
-    "ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-    },
-    "next-tick": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
-    },
-    "node-gyp-build": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
-      "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg=="
-    },
-    "type": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
-    },
-    "typedarray-to-buffer": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
-      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
-      "requires": {
-        "is-typedarray": "^1.0.0"
-      }
-    },
-    "utf-8-validate": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz",
-      "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==",
-      "requires": {
-        "node-gyp-build": "^4.2.0"
-      }
-    },
-    "websocket": {
-      "version": "1.0.33",
-      "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz",
-      "integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==",
-      "requires": {
-        "bufferutil": "^4.0.1",
-        "debug": "^2.2.0",
-        "es5-ext": "^0.10.50",
-        "typedarray-to-buffer": "^3.1.5",
-        "utf-8-validate": "^5.0.2",
-        "yaeti": "^0.0.6"
-      }
-    },
-    "yaeti": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
-      "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
-    }
-  }
-}

+ 4 - 4
include/rtc/candidate.hpp

@@ -19,7 +19,7 @@
 #ifndef RTC_CANDIDATE_H
 #ifndef RTC_CANDIDATE_H
 #define RTC_CANDIDATE_H
 #define RTC_CANDIDATE_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 #include <string>
 #include <string>
 
 
@@ -52,8 +52,8 @@ public:
 
 
 	bool isResolved() const;
 	bool isResolved() const;
 	Family family() const;
 	Family family() const;
-	std::optional<string> address() const;
-	std::optional<uint16_t> port() const;
+	optional<string> address() const;
+	optional<uint16_t> port() const;
 
 
 private:
 private:
 	void parse(string candidate);
 	void parse(string candidate);
@@ -66,7 +66,7 @@ private:
 	string mNode, mService;
 	string mNode, mService;
 	string mTail;
 	string mTail;
 
 
-	std::optional<string> mMid;
+	optional<string> mMid;
 
 
 	// Extracted on resolution
 	// Extracted on resolution
 	Family mFamily;
 	Family mFamily;

+ 12 - 27
include/rtc/channel.hpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2019-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
@@ -19,19 +19,21 @@
 #ifndef RTC_CHANNEL_H
 #ifndef RTC_CHANNEL_H
 #define RTC_CHANNEL_H
 #define RTC_CHANNEL_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "message.hpp"
 #include "message.hpp"
 
 
 #include <atomic>
 #include <atomic>
 #include <functional>
 #include <functional>
-#include <variant>
 
 
 namespace rtc {
 namespace rtc {
 
 
-class RTC_CPP_EXPORT Channel {
+namespace impl {
+struct Channel;
+}
+
+class RTC_CPP_EXPORT Channel : private CheshireCat<impl::Channel> {
 public:
 public:
-	Channel() = default;
-	virtual ~Channel() = default;
+	virtual ~Channel();
 
 
 	virtual void close() = 0;
 	virtual void close() = 0;
 	virtual bool send(message_variant data) = 0; // returns false if buffered
 	virtual bool send(message_variant data) = 0; // returns false if buffered
@@ -54,30 +56,13 @@ public:
 	void setBufferedAmountLowThreshold(size_t amount);
 	void setBufferedAmountLowThreshold(size_t amount);
 
 
 	// Extended API
 	// Extended API
-	virtual std::optional<message_variant> receive() = 0; // only if onMessage unset
-	virtual std::optional<message_variant> peek() = 0;    // only if onMessage unset
-	virtual size_t availableAmount() const;               // total size available to receive
+	optional<message_variant> receive(); // only if onMessage unset
+	optional<message_variant> peek();    // only if onMessage unset
+	size_t availableAmount() const;           // total size available to receive
 	void onAvailable(std::function<void()> callback);
 	void onAvailable(std::function<void()> callback);
 
 
 protected:
 protected:
-	virtual void triggerOpen();
-	virtual void triggerClosed();
-	virtual void triggerError(string error);
-	virtual void triggerAvailable(size_t count);
-	virtual void triggerBufferedAmount(size_t amount);
-
-	void resetCallbacks();
-
-private:
-	synchronized_callback<> mOpenCallback;
-	synchronized_callback<> mClosedCallback;
-	synchronized_callback<string> mErrorCallback;
-	synchronized_callback<message_variant> mMessageCallback;
-	synchronized_callback<> mAvailableCallback;
-	synchronized_callback<> mBufferedAmountLowCallback;
-
-	std::atomic<size_t> mBufferedAmount = 0;
-	std::atomic<size_t> mBufferedAmountLowThreshold = 0;
+	Channel(impl_ptr<impl::Channel> impl);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 80 - 0
include/rtc/common.hpp

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2019 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
+ */
+
+#ifndef RTC_COMMON_H
+#define RTC_COMMON_H
+
+#ifndef RTC_ENABLE_WEBSOCKET
+#define RTC_ENABLE_WEBSOCKET 1
+#endif
+
+#ifndef RTC_ENABLE_MEDIA
+#define RTC_ENABLE_MEDIA 1
+#endif
+
+#ifdef _WIN32
+#define RTC_CPP_EXPORT __declspec(dllexport)
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0602 // Windows 8
+#endif
+#ifdef _MSC_VER
+#pragma warning(disable : 4251) // disable "X needs to have dll-interface..."
+#endif
+#else
+#define RTC_CPP_EXPORT
+#endif
+
+#include "rtc.h" // for C API defines
+
+#include "log.hpp"
+#include "utils.hpp"
+
+#include <cstddef>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <variant>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace rtc {
+
+using std::byte;
+using std::nullopt;
+using std::optional;
+using std::shared_ptr;
+using std::string;
+using std::string_view;
+using std::unique_ptr;
+using std::variant;
+using std::weak_ptr;
+
+using binary = std::vector<byte>;
+using binary_ptr = std::shared_ptr<binary>;
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+} // namespace rtc
+
+#endif

+ 7 - 5
include/rtc/configuration.hpp

@@ -19,7 +19,7 @@
 #ifndef RTC_ICE_CONFIGURATION_H
 #ifndef RTC_ICE_CONFIGURATION_H
 #define RTC_ICE_CONFIGURATION_H
 #define RTC_ICE_CONFIGURATION_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "message.hpp"
 #include "message.hpp"
 
 
 #include <vector>
 #include <vector>
@@ -44,7 +44,7 @@ struct RTC_CPP_EXPORT IceServer {
 	          RelayType relayType_ = RelayType::TurnUdp);
 	          RelayType relayType_ = RelayType::TurnUdp);
 
 
 	string hostname;
 	string hostname;
-	string service;
+	uint16_t port;
 	Type type;
 	Type type;
 	string username;
 	string username;
 	string password;
 	string password;
@@ -54,11 +54,11 @@ struct RTC_CPP_EXPORT IceServer {
 struct RTC_CPP_EXPORT ProxyServer {
 struct RTC_CPP_EXPORT ProxyServer {
 	enum class Type { None = 0, Socks5, Http, Last = Http };
 	enum class Type { None = 0, Socks5, Http, Last = Http };
 
 
-	ProxyServer(Type type_, string ip_, uint16_t port_, string username_ = "",
+	ProxyServer(Type type_, string hostname_, uint16_t port_, string username_ = "",
 	            string password_ = "");
 	            string password_ = "");
 
 
 	Type type;
 	Type type;
-	string ip;
+	string hostname;
 	uint16_t port;
 	uint16_t port;
 	string username;
 	string username;
 	string password;
 	string password;
@@ -66,10 +66,12 @@ struct RTC_CPP_EXPORT ProxyServer {
 
 
 struct RTC_CPP_EXPORT Configuration {
 struct RTC_CPP_EXPORT Configuration {
 	std::vector<IceServer> iceServers;
 	std::vector<IceServer> iceServers;
-	std::optional<ProxyServer> proxyServer;
+	optional<ProxyServer> proxyServer;
 	bool enableIceTcp = false;
 	bool enableIceTcp = false;
+	bool disableAutoNegotiation = false;
 	uint16_t portRangeBegin = 1024;
 	uint16_t portRangeBegin = 1024;
 	uint16_t portRangeEnd = 65535;
 	uint16_t portRangeEnd = 65535;
+	optional<size_t> mtu;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 20 - 60
include/rtc/datachannel.hpp

@@ -20,27 +20,29 @@
 #define RTC_DATA_CHANNEL_H
 #define RTC_DATA_CHANNEL_H
 
 
 #include "channel.hpp"
 #include "channel.hpp"
-#include "include.hpp"
+#include "common.hpp"
 #include "message.hpp"
 #include "message.hpp"
-#include "queue.hpp"
 #include "reliability.hpp"
 #include "reliability.hpp"
 
 
 #include <atomic>
 #include <atomic>
 #include <chrono>
 #include <chrono>
 #include <functional>
 #include <functional>
+#include <shared_mutex>
 #include <type_traits>
 #include <type_traits>
-#include <variant>
+#include <shared_mutex>
 
 
 namespace rtc {
 namespace rtc {
 
 
-class SctpTransport;
-class PeerConnection;
+namespace impl {
 
 
-class RTC_CPP_EXPORT DataChannel : public std::enable_shared_from_this<DataChannel>,
-                                   public Channel {
+struct DataChannel;
+struct PeerConnection;
+
+} // namespace impl
+
+class RTC_CPP_EXPORT DataChannel final : private CheshireCat<impl::DataChannel>, public Channel {
 public:
 public:
-	DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
-	            Reliability reliability);
+	DataChannel(impl_ptr<impl::DataChannel> impl);
 	virtual ~DataChannel();
 	virtual ~DataChannel();
 
 
 	uint16_t stream() const;
 	uint16_t stream() const;
@@ -49,58 +51,18 @@ public:
 	string protocol() const;
 	string protocol() const;
 	Reliability reliability() const;
 	Reliability reliability() const;
 
 
+	bool isOpen(void) const override;
+	bool isClosed(void) const override;
+	size_t maxMessageSize() const override;
+
 	void close(void) override;
 	void close(void) override;
 	bool send(message_variant data) override;
 	bool send(message_variant data) override;
 	bool send(const byte *data, size_t size) override;
 	bool send(const byte *data, size_t size) override;
 	template <typename Buffer> bool sendBuffer(const Buffer &buf);
 	template <typename Buffer> bool sendBuffer(const Buffer &buf);
 	template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
 	template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
 
 
-	bool isOpen(void) const override;
-	bool isClosed(void) const override;
-	size_t maxMessageSize() const override;
-
-	// Extended API
-	size_t availableAmount() const override;
-	std::optional<message_variant> receive() override;
-	std::optional<message_variant> peek() override;
-
-protected:
-	virtual void open(std::shared_ptr<SctpTransport> transport);
-	virtual void processOpenMessage(message_ptr message);
-	void remoteClose();
-	bool outgoing(message_ptr message);
-	void incoming(message_ptr message);
-
-	const std::weak_ptr<PeerConnection> mPeerConnection;
-	std::weak_ptr<SctpTransport> mSctpTransport;
-
-	uint16_t mStream;
-	string mLabel;
-	string mProtocol;
-	std::shared_ptr<Reliability> mReliability;
-
-	std::atomic<bool> mIsOpen = false;
-	std::atomic<bool> mIsClosed = false;
-
 private:
 private:
-	Queue<message_ptr> mRecvQueue;
-
-	friend class PeerConnection;
-};
-
-class RTC_CPP_EXPORT NegociatedDataChannel final : public DataChannel {
-public:
-	NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
-	                      string protocol, Reliability reliability);
-	NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, std::weak_ptr<SctpTransport> transport,
-	                      uint16_t stream);
-	~NegociatedDataChannel();
-
-private:
-	void open(std::shared_ptr<SctpTransport> transport) override;
-	void processOpenMessage(message_ptr message) override;
-
-	friend class PeerConnection;
+	using CheshireCat<impl::DataChannel>::impl;
 };
 };
 
 
 template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer &buf) {
 template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer &buf) {
@@ -112,9 +74,7 @@ template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer
 
 
 template <typename Buffer> bool DataChannel::sendBuffer(const Buffer &buf) {
 template <typename Buffer> bool DataChannel::sendBuffer(const Buffer &buf) {
 	auto [bytes, size] = to_bytes(buf);
 	auto [bytes, size] = to_bytes(buf);
-	auto message = std::make_shared<Message>(size);
-	std::copy(bytes, bytes + size, message->data());
-	return outgoing(message);
+	return send(bytes, size);
 }
 }
 
 
 template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterator last) {
 template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterator last) {
@@ -122,13 +82,13 @@ template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterat
 	for (Iterator it = first; it != last; ++it)
 	for (Iterator it = first; it != last; ++it)
 		size += it->size();
 		size += it->size();
 
 
-	auto message = std::make_shared<Message>(size);
-	auto pos = message->begin();
+	binary buffer(size);
+	byte *pos = buffer.data();
 	for (Iterator it = first; it != last; ++it) {
 	for (Iterator it = first; it != last; ++it) {
 		auto [bytes, len] = to_bytes(*it);
 		auto [bytes, len] = to_bytes(*it);
 		pos = std::copy(bytes, bytes + len, pos);
 		pos = std::copy(bytes, bytes + len, pos);
 	}
 	}
-	return outgoing(message);
+	return send(std::move(buffer));
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc

+ 24 - 27
include/rtc/description.hpp

@@ -21,13 +21,10 @@
 #define RTC_DESCRIPTION_H
 #define RTC_DESCRIPTION_H
 
 
 #include "candidate.hpp"
 #include "candidate.hpp"
-#include "include.hpp"
+#include "common.hpp"
 
 
 #include <iostream>
 #include <iostream>
 #include <map>
 #include <map>
-#include <memory>
-#include <optional>
-#include <variant>
 #include <vector>
 #include <vector>
 
 
 namespace rtc {
 namespace rtc {
@@ -54,9 +51,9 @@ public:
 	string typeString() const;
 	string typeString() const;
 	Role role() const;
 	Role role() const;
 	string bundleMid() const;
 	string bundleMid() const;
-	std::optional<string> iceUfrag() const;
-	std::optional<string> icePwd() const;
-	std::optional<string> fingerprint() const;
+	optional<string> iceUfrag() const;
+	optional<string> icePwd() const;
+	optional<string> fingerprint() const;
 	bool ended() const;
 	bool ended() const;
 
 
 	void hintType(Type type);
 	void hintType(Type type);
@@ -116,16 +113,16 @@ public:
 		void hintSctpPort(uint16_t port) { mSctpPort = mSctpPort.value_or(port); }
 		void hintSctpPort(uint16_t port) { mSctpPort = mSctpPort.value_or(port); }
 		void setMaxMessageSize(size_t size) { mMaxMessageSize = size; }
 		void setMaxMessageSize(size_t size) { mMaxMessageSize = size; }
 
 
-		std::optional<uint16_t> sctpPort() const { return mSctpPort; }
-		std::optional<size_t> maxMessageSize() const { return mMaxMessageSize; }
+		optional<uint16_t> sctpPort() const { return mSctpPort; }
+		optional<size_t> maxMessageSize() const { return mMaxMessageSize; }
 
 
 		virtual void parseSdpLine(string_view line) override;
 		virtual void parseSdpLine(string_view line) override;
 
 
 	private:
 	private:
 		virtual string generateSdpLines(string_view eol) const override;
 		virtual string generateSdpLines(string_view eol) const override;
 
 
-		std::optional<uint16_t> mSctpPort;
-		std::optional<size_t> mMaxMessageSize;
+		optional<uint16_t> mSctpPort;
+		optional<size_t> mMaxMessageSize;
 	};
 	};
 
 
 	// Media (non-data)
 	// Media (non-data)
@@ -140,10 +137,10 @@ public:
 
 
 		void removeFormat(const string &fmt);
 		void removeFormat(const string &fmt);
 
 
-		void addSSRC(uint32_t ssrc, std::optional<string> name,
-		             std::optional<string> msid = nullopt);
-		void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
-		                 std::optional<string> msid = nullopt);
+		void addSSRC(uint32_t ssrc, optional<string> name,
+					 optional<string> msid = nullopt, optional<string> trackID = nullopt);
+		void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
+						 optional<string> msid = nullopt, optional<string> trackID = nullopt);
 		bool hasSSRC(uint32_t ssrc);
 		bool hasSSRC(uint32_t ssrc);
 		std::vector<uint32_t> getSSRCs();
 		std::vector<uint32_t> getSSRCs();
 
 
@@ -203,10 +200,10 @@ public:
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
 
 
 		void addAudioCodec(int payloadType, string codec,
 		void addAudioCodec(int payloadType, string codec,
-		                   std::optional<string> profile = std::nullopt);
+		                   optional<string> profile = std::nullopt);
 
 
 		void addOpusCodec(int payloadType,
 		void addOpusCodec(int payloadType,
-		                  std::optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
+		                  optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
 	};
 	};
 
 
 	class RTC_CPP_EXPORT Video : public Media {
 	class RTC_CPP_EXPORT Video : public Media {
@@ -214,10 +211,10 @@ public:
 		Video(string mid = "video", Direction dir = Direction::SendOnly);
 		Video(string mid = "video", Direction dir = Direction::SendOnly);
 
 
 		void addVideoCodec(int payloadType, string codec,
 		void addVideoCodec(int payloadType, string codec,
-		                   std::optional<string> profile = std::nullopt);
+		                   optional<string> profile = std::nullopt);
 
 
 		void addH264Codec(int payloadType,
 		void addH264Codec(int payloadType,
-		                  std::optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
+		                  optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
 		void addVP8Codec(int payloadType);
 		void addVP8Codec(int payloadType);
 		void addVP9Codec(int payloadType);
 		void addVP9Codec(int payloadType);
 	};
 	};
@@ -232,8 +229,8 @@ public:
 	int addVideo(string mid = "video", Direction dir = Direction::SendOnly);
 	int addVideo(string mid = "video", Direction dir = Direction::SendOnly);
 	int addAudio(string mid = "audio", Direction dir = Direction::SendOnly);
 	int addAudio(string mid = "audio", Direction dir = Direction::SendOnly);
 
 
-	std::variant<Media *, Application *> media(unsigned int index);
-	std::variant<const Media *, const Application *> media(unsigned int index) const;
+	variant<Media *, Application *> media(unsigned int index);
+	variant<const Media *, const Application *> media(unsigned int index) const;
 	unsigned int mediaCount() const;
 	unsigned int mediaCount() const;
 
 
 	Application *application();
 	Application *application();
@@ -242,8 +239,8 @@ public:
 	static string typeToString(Type type);
 	static string typeToString(Type type);
 
 
 private:
 private:
-	std::optional<Candidate> defaultCandidate() const;
-	std::shared_ptr<Entry> createEntry(string mline, string mid, Direction dir);
+	optional<Candidate> defaultCandidate() const;
+	shared_ptr<Entry> createEntry(string mline, string mid, Direction dir);
 	void removeApplication();
 	void removeApplication();
 
 
 	Type mType;
 	Type mType;
@@ -252,12 +249,12 @@ private:
 	Role mRole;
 	Role mRole;
 	string mUsername;
 	string mUsername;
 	string mSessionId;
 	string mSessionId;
-	std::optional<string> mIceUfrag, mIcePwd;
-	std::optional<string> mFingerprint;
+	optional<string> mIceUfrag, mIcePwd;
+	optional<string> mFingerprint;
 
 
 	// Entries
 	// Entries
-	std::vector<std::shared_ptr<Entry>> mEntries;
-	std::shared_ptr<Application> mApplication;
+	std::vector<shared_ptr<Entry>> mEntries;
+	shared_ptr<Application> mApplication;
 
 
 	// Candidates
 	// Candidates
 	std::vector<Candidate> mCandidates;
 	std::vector<Candidate> mCandidates;

+ 3 - 37
include/rtc/h264packetizationhandler.hpp

@@ -23,50 +23,16 @@
 
 
 #include "h264rtppacketizer.hpp"
 #include "h264rtppacketizer.hpp"
 #include "nalunit.hpp"
 #include "nalunit.hpp"
-#include "rtcphandler.hpp"
-#include "rtcpsenderreporter.hpp"
+#include "mediachainablehandler.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
 /// Handler for H264 packetization
 /// Handler for H264 packetization
-class RTC_CPP_EXPORT H264PacketizationHandler : public RtcpHandler, public RtcpSenderReporter {
-	/// RTP packetizer for H264
-	const std::shared_ptr<H264RtpPacketizer> packetizer;
-
-	const uint16_t maximumFragmentSize;
-
-	std::shared_ptr<NalUnits> splitMessage(message_ptr message);
-
+class RTC_CPP_EXPORT H264PacketizationHandler : public MediaChainableHandler {
 public:
 public:
-	/// Nalunit separator
-	enum class Separator {
-		LongStartSequence,  // 0x00, 0x00, 0x00, 0x01
-		ShortStartSequence, // 0x00, 0x00, 0x01
-		StartSequence,      // LongStartSequence or ShortStartSequence
-		Length              // first 4 bytes is nal unit length
-	};
-
 	/// Construct handler for H264 packetization.
 	/// Construct handler for H264 packetization.
-	/// @param separator Nal units separator
 	/// @param packetizer RTP packetizer for h264
 	/// @param packetizer RTP packetizer for h264
-	H264PacketizationHandler(Separator separator, std::shared_ptr<H264RtpPacketizer> packetizer,
-	                         uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
-
-	/// Returns message unchanged
-	/// @param ptr message
-	message_ptr incoming(message_ptr ptr) override;
-
-	/// Returns packetized message if message type is binary
-	/// @note NAL units in `ptr` message must be separated by `separator` given in constructor
-	/// @note If message generates multiple rtp packets, all but last are send using
-	/// `outgoingCallback`. It is your responsibility to send last packet.
-	/// @param ptr message containing all NAL units for current timestamp (one sample)
-	/// @return last packet
-	message_ptr outgoing(message_ptr ptr) override;
-
-private:
-	/// Separator
-	const Separator separator;
+	H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 26 - 5
include/rtc/h264rtppacketizer.hpp

@@ -16,31 +16,52 @@
  * along with this program; If not, see <http://www.gnu.org/licenses/>.
  * along with this program; If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-#ifndef H264RtpPacketizer_hpp
-#define H264RtpPacketizer_hpp
+#ifndef H264_RTP_PACKETIZER_H
+#define H264_RTP_PACKETIZER_H
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
+#include "nalunit.hpp"
 #include "rtppacketizer.hpp"
 #include "rtppacketizer.hpp"
+#include "mediahandlerrootelement.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
 /// RTP packetization of h264 payload
 /// RTP packetization of h264 payload
-class RTC_CPP_EXPORT H264RtpPacketizer : public RtpPacketizer {
+class RTC_CPP_EXPORT H264RtpPacketizer : public RtpPacketizer, public MediaHandlerRootElement {
+	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+	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;
 	static const auto defaultClockRate = 90 * 1000;
 
 
+	/// Nalunit separator
+	enum class Separator {
+		LongStartSequence,  // 0x00, 0x00, 0x00, 0x01
+		ShortStartSequence, // 0x00, 0x00, 0x01
+		StartSequence,      // LongStartSequence or ShortStartSequence
+		Length              // first 4 bytes is nal unit length
+	};
+
+	H264RtpPacketizer(H264RtpPacketizer::Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
+					  uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
 	/// Constructs h264 payload packetizer with given RTP configuration.
 	/// Constructs h264 payload 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
 	/// properties such as sequence number.
 	/// properties such as sequence number.
 	/// @param rtpConfig  RTP configuration
 	/// @param rtpConfig  RTP configuration
-	H264RtpPacketizer(std::shared_ptr<RtpPacketizationConfig> rtpConfig);
+	/// @param maximumFragmentSize maximum size of one NALU fragment
+	H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+					  uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
+	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) override;
+private:
+	const Separator separator;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc
 
 
 #endif /* RTC_ENABLE_MEDIA */
 #endif /* RTC_ENABLE_MEDIA */
 
 
-#endif /* H264RtpPacketizer_hpp */
+#endif /* H264_RTP_PACKETIZER_H */

+ 4 - 4
include/rtc/init.hpp

@@ -19,13 +19,13 @@
 #ifndef RTC_INIT_H
 #ifndef RTC_INIT_H
 #define RTC_INIT_H
 #define RTC_INIT_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 #include <mutex>
 #include <mutex>
 
 
 namespace rtc {
 namespace rtc {
 
 
-using init_token = std::shared_ptr<void>;
+using init_token = shared_ptr<void>;
 
 
 class RTC_CPP_EXPORT Init {
 class RTC_CPP_EXPORT Init {
 public:
 public:
@@ -38,8 +38,8 @@ public:
 private:
 private:
 	Init();
 	Init();
 
 
-	static std::weak_ptr<void> Weak;
-	static std::shared_ptr<void> *Global;
+	static weak_ptr<void> Weak;
+	static shared_ptr<void> *Global;
 	static bool Initialized;
 	static bool Initialized;
 	static std::recursive_mutex Mutex;
 	static std::recursive_mutex Mutex;
 };
 };

+ 1 - 1
include/rtc/log.hpp

@@ -35,7 +35,7 @@
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 #endif
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 

+ 55 - 0
include/rtc/mediachainablehandler.hpp

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTC_MEDIA_CHAINABLE_HANDLER_H
+#define RTC_MEDIA_CHAINABLE_HANDLER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandler.hpp"
+#include "mediahandlerrootelement.hpp"
+
+namespace rtc {
+
+class RTC_CPP_EXPORT MediaChainableHandler : public MediaHandler {
+	const shared_ptr<MediaHandlerRootElement> root;
+	shared_ptr<MediaHandlerElement> leaf;
+	std::mutex inoutMutex;
+
+	message_ptr handleIncomingBinary(message_ptr);
+	message_ptr handleIncomingControl(message_ptr);
+	message_ptr handleOutgoingBinary(message_ptr);
+	message_ptr handleOutgoingControl(message_ptr);
+	bool sendProduct(ChainedOutgoingProduct product);
+public:
+	MediaChainableHandler(shared_ptr<MediaHandlerRootElement> root);
+	~MediaChainableHandler();
+	message_ptr incoming(message_ptr ptr) override;
+	message_ptr outgoing(message_ptr ptr) override;
+
+	bool send(message_ptr msg);
+
+	/// Adds element to chain
+	/// @param chainable Chainable element
+    void addToChain(shared_ptr<MediaHandlerElement> chainable);
+};
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif // RTC_MEDIA_CHAINABLE_HANDLER_H

+ 5 - 5
include/rtc/rtcphandler.hpp → include/rtc/mediahandler.hpp

@@ -17,15 +17,15 @@
  * 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_RTCP_HANDLER_H
-#define RTC_RTCP_HANDLER_H
+#ifndef RTC_MEDIA_HANDLER_H
+#define RTC_MEDIA_HANDLER_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "message.hpp"
 #include "message.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-class RTC_CPP_EXPORT RtcpHandler {
+class RTC_CPP_EXPORT MediaHandler {
 protected:
 protected:
 	// Use this callback when trying to send custom data (such as RTCP) to the client.
 	// Use this callback when trying to send custom data (such as RTCP) to the client.
 	synchronized_callback<message_ptr> outgoingCallback;
 	synchronized_callback<message_ptr> outgoingCallback;
@@ -47,4 +47,4 @@ public:
 
 
 } // namespace rtc
 } // namespace rtc
 
 
-#endif // RTC_RTCP_HANDLER_H
+#endif // RTC_MEDIA_HANDLER_H

+ 111 - 0
include/rtc/mediahandlerelement.hpp

@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTC_MEDIA_HANDLER_ELEMENT_H
+#define RTC_MEDIA_HANDLER_ELEMENT_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "common.hpp"
+#include "message.hpp"
+#include "rtp.hpp"
+
+namespace rtc {
+
+using ChainedMessagesProduct = shared_ptr<std::vector<binary_ptr>>;
+
+RTC_CPP_EXPORT ChainedMessagesProduct make_chained_messages_product();
+RTC_CPP_EXPORT ChainedMessagesProduct make_chained_messages_product(message_ptr msg);
+
+/// Ougoing messages
+struct RTC_CPP_EXPORT ChainedOutgoingProduct {
+	ChainedOutgoingProduct(ChainedMessagesProduct messages = nullptr, message_ptr control = nullptr);
+	const ChainedMessagesProduct messages;
+	const message_ptr control;
+};
+
+/// Incoming messages with response
+struct RTC_CPP_EXPORT ChainedIncomingProduct {
+	ChainedIncomingProduct(ChainedMessagesProduct incoming = nullptr, ChainedMessagesProduct outgoing = nullptr);
+	const ChainedMessagesProduct incoming;
+	const ChainedOutgoingProduct outgoing;
+};
+
+/// Incoming control messages with response
+struct RTC_CPP_EXPORT ChainedIncomingControlProduct {
+	ChainedIncomingControlProduct(message_ptr incoming, optional<ChainedOutgoingProduct> outgoing = nullopt);
+	const message_ptr incoming;
+	const optional<ChainedOutgoingProduct> outgoing;
+};
+
+/// Chainable handler
+class RTC_CPP_EXPORT MediaHandlerElement: public std::enable_shared_from_this<MediaHandlerElement> {
+	shared_ptr<MediaHandlerElement> upstream = nullptr;
+	shared_ptr<MediaHandlerElement> downstream = nullptr;
+
+	void prepareAndSendResponse(optional<ChainedOutgoingProduct> outgoing, std::function<bool (ChainedOutgoingProduct)> send);
+
+	void removeFromChain();
+public:
+	MediaHandlerElement();
+
+	/// Creates response to incoming message
+	/// @param messages Current repsonse
+	/// @returns New response
+	optional<ChainedOutgoingProduct> processOutgoingResponse(ChainedOutgoingProduct messages);
+
+	// Process incoming and ougoing messages
+	message_ptr formIncomingControlMessage(message_ptr message, std::function<bool (ChainedOutgoingProduct)> send);
+	ChainedMessagesProduct formIncomingBinaryMessage(ChainedMessagesProduct messages, std::function<bool (ChainedOutgoingProduct)> send);
+	message_ptr formOutgoingControlMessage(message_ptr message);
+	optional<ChainedOutgoingProduct> formOutgoingBinaryMessage(ChainedOutgoingProduct product);
+
+	/// Process current control message
+	/// @param messages current message
+	/// @returns Modified message and response
+	virtual ChainedIncomingControlProduct processIncomingControlMessage(message_ptr messages);
+
+	/// Process current control message
+	/// @param messages current message
+	/// @returns Modified message
+	virtual message_ptr processOutgoingControlMessage(message_ptr messages);
+
+	/// Process current binary message
+	/// @param messages current message
+	/// @returns Modified message and response
+	virtual ChainedIncomingProduct processIncomingBinaryMessage(ChainedMessagesProduct messages);
+
+	/// Process current binary message
+	/// @param messages current message
+	/// @param control current control message
+	/// @returns Modified binary message and control message
+	virtual ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control);
+
+	/// Set given element as upstream to this
+	/// @param upstream Upstream element
+	/// @returns Upstream element
+	shared_ptr<MediaHandlerElement> chainWith(shared_ptr<MediaHandlerElement> upstream);
+
+	/// Remove all downstream elements from chain
+	void recursiveRemoveChain();
+};
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif // RTC_MEDIA_HANDLER_ELEMENT_H

+ 45 - 0
include/rtc/mediahandlerrootelement.hpp

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H
+#define RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandlerelement.hpp"
+
+namespace rtc {
+
+/// Chainable message handler
+class RTC_CPP_EXPORT MediaHandlerRootElement : public MediaHandlerElement {
+public:
+	MediaHandlerRootElement() { }
+
+	/// Reduce multiple messages into one message
+	/// @param messages Messages to reduce
+	virtual message_ptr reduce(ChainedMessagesProduct messages);
+
+	/// Splits message into multiple messages
+	/// @param message Message to split
+	virtual ChainedMessagesProduct split(message_ptr message);
+};
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif // RTCP_MEDIA_HANDLER_ROOT_ELEMENT_H

+ 7 - 10
include/rtc/message.hpp

@@ -19,13 +19,10 @@
 #ifndef RTC_MESSAGE_H
 #ifndef RTC_MESSAGE_H
 #define RTC_MESSAGE_H
 #define RTC_MESSAGE_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "reliability.hpp"
 #include "reliability.hpp"
 
 
 #include <functional>
 #include <functional>
-#include <memory>
-#include <optional>
-#include <variant>
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -44,12 +41,12 @@ struct RTC_CPP_EXPORT Message : binary {
 	Type type;
 	Type type;
 	unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
 	unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
 	unsigned int dscp = 0;   // Differentiated Services Code Point
 	unsigned int dscp = 0;   // Differentiated Services Code Point
-	std::shared_ptr<Reliability> reliability;
+	shared_ptr<Reliability> reliability;
 };
 };
 
 
-using message_ptr = std::shared_ptr<Message>;
+using message_ptr = shared_ptr<Message>;
 using message_callback = std::function<void(message_ptr message)>;
 using message_callback = std::function<void(message_ptr message)>;
-using message_variant = std::variant<binary, string>;
+using message_variant = variant<binary, string>;
 
 
 inline size_t message_size_func(const message_ptr &m) {
 inline size_t message_size_func(const message_ptr &m) {
 	return m->type == Message::Binary || m->type == Message::String ? m->size() : 0;
 	return m->type == Message::Binary || m->type == Message::String ? m->size() : 0;
@@ -58,7 +55,7 @@ inline size_t message_size_func(const message_ptr &m) {
 template <typename Iterator>
 template <typename Iterator>
 message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary,
 message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary,
                          unsigned int stream = 0,
                          unsigned int stream = 0,
-                         std::shared_ptr<Reliability> reliability = nullptr) {
+                         shared_ptr<Reliability> reliability = nullptr) {
 	auto message = std::make_shared<Message>(begin, end, type);
 	auto message = std::make_shared<Message>(begin, end, type);
 	message->stream = stream;
 	message->stream = stream;
 	message->reliability = reliability;
 	message->reliability = reliability;
@@ -67,11 +64,11 @@ message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Mess
 
 
 RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Message::Binary,
 RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Message::Binary,
                                         unsigned int stream = 0,
                                         unsigned int stream = 0,
-                                        std::shared_ptr<Reliability> reliability = nullptr);
+                                        shared_ptr<Reliability> reliability = nullptr);
 
 
 RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary,
 RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary,
                                         unsigned int stream = 0,
                                         unsigned int stream = 0,
-                                        std::shared_ptr<Reliability> reliability = nullptr);
+                                        shared_ptr<Reliability> reliability = nullptr);
 
 
 RTC_CPP_EXPORT message_ptr make_message(message_variant data);
 RTC_CPP_EXPORT message_ptr make_message(message_variant data);
 
 

+ 12 - 13
include/rtc/nalunit.hpp

@@ -16,12 +16,12 @@
  * along with this program; If not, see <http://www.gnu.org/licenses/>.
  * along with this program; If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-#ifndef NalUnit_hpp
-#define NalUnit_hpp
+#ifndef NAL_UNIT_H
+#define NAL_UNIT_H
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -62,11 +62,9 @@ private:
 /// Nal unit
 /// Nal unit
 struct RTC_CPP_EXPORT NalUnit : binary {
 struct RTC_CPP_EXPORT NalUnit : binary {
 	NalUnit(const NalUnit &unit) = default;
 	NalUnit(const NalUnit &unit) = default;
-	NalUnit(size_t size, bool includingHeader = true)
-	    : binary(size + (includingHeader ? 0 : 1)) {}
+	NalUnit(size_t size, bool includingHeader = true) : binary(size + (includingHeader ? 0 : 1)) {}
 
 
-	template <typename Iterator>
-	NalUnit(Iterator begin_, Iterator end_) : binary(begin_, end_) {}
+	template <typename Iterator> NalUnit(Iterator begin_, Iterator end_) : binary(begin_, end_) {}
 
 
 	NalUnit(binary &&data) : binary(std::move(data)) {}
 	NalUnit(binary &&data) : binary(std::move(data)) {}
 
 
@@ -103,7 +101,8 @@ struct RTC_CPP_EXPORT NalUnitFragmentA : NalUnit {
 	NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType,
 	NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType,
 	                 binary data);
 	                 binary data);
 
 
-	static std::vector<NalUnitFragmentA> fragmentsFrom(NalUnit nalu, uint16_t maximumFragmentSize);
+	static std::vector<shared_ptr<NalUnitFragmentA>>
+	fragmentsFrom(shared_ptr<NalUnit> nalu, uint16_t maximumFragmentSize);
 
 
 	uint8_t unitType() { return fragmentHeader()->unitType(); }
 	uint8_t unitType() { return fragmentHeader()->unitType(); }
 
 
@@ -142,15 +141,15 @@ protected:
 	const uint8_t nal_type_fu_A = 28;
 	const uint8_t nal_type_fu_A = 28;
 };
 };
 
 
-class RTC_CPP_EXPORT NalUnits : public std::vector<NalUnit> {
+class RTC_CPP_EXPORT NalUnits : public std::vector<shared_ptr<NalUnit>> {
 public:
 public:
-	static const uint16_t defaultMaximumFragmentSize = 1400;
-	std::vector<binary>
-	generateFragments(uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+	static const uint16_t defaultMaximumFragmentSize =
+	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6
+	std::vector<shared_ptr<binary>> generateFragments(uint16_t maximumFragmentSize);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc
 
 
 #endif /* RTC_ENABLE_MEDIA */
 #endif /* RTC_ENABLE_MEDIA */
 
 
-#endif /* NalUnit_hpp */
+#endif /* NAL_UNIT_H */

+ 3 - 13
include/rtc/opuspacketizationhandler.hpp

@@ -22,27 +22,17 @@
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
 #include "opusrtppacketizer.hpp"
 #include "opusrtppacketizer.hpp"
-#include "rtcphandler.hpp"
-#include "rtcpsenderreporter.hpp"
+#include "mediachainablehandler.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
 /// Handler for opus packetization
 /// Handler for opus packetization
-class RTC_CPP_EXPORT OpusPacketizationHandler : public RtcpHandler, public RtcpSenderReporter {
-	/// RTP packetizer for opus
-	const std::shared_ptr<OpusRtpPacketizer> packetizer;
+class RTC_CPP_EXPORT OpusPacketizationHandler : public MediaChainableHandler {
 
 
 public:
 public:
 	/// Construct handler for opus packetization.
 	/// Construct handler for opus packetization.
 	/// @param packetizer RTP packetizer for opus
 	/// @param packetizer RTP packetizer for opus
-	OpusPacketizationHandler(std::shared_ptr<OpusRtpPacketizer> packetizer);
-
-	/// Returns message unchanged
-	/// @param ptr message
-	message_ptr incoming(message_ptr ptr) override;
-	/// Returns packetized message if message type is binary
-	/// @param ptr message
-	message_ptr outgoing(message_ptr ptr) override;
+	OpusPacketizationHandler(shared_ptr<OpusRtpPacketizer> packetizer);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 10 - 4
include/rtc/opusrtppacketizer.hpp

@@ -22,12 +22,12 @@
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
 #include "rtppacketizer.hpp"
 #include "rtppacketizer.hpp"
+#include "mediahandlerrootelement.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
 /// RTP packetizer for opus
 /// RTP packetizer for opus
-class RTC_CPP_EXPORT OpusRtpPacketizer : public RtpPacketizer {
-
+class RTC_CPP_EXPORT OpusRtpPacketizer : 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;
 	static const uint32_t defaultClockRate = 48 * 1000;
@@ -36,13 +36,19 @@ public:
 	/// @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
 	/// properties such as sequence number.
 	/// properties such as sequence number.
 	/// @param rtpConfig  RTP configuration
 	/// @param rtpConfig  RTP configuration
-	OpusRtpPacketizer(std::shared_ptr<RtpPacketizationConfig> rtpConfig);
+	OpusRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig);
 
 
 	/// Creates RTP packet for given payload based on `rtpConfig`.
 	/// Creates RTP packet for given payload based on `rtpConfig`.
 	/// @note This function increase sequence number after packetization.
 	/// @note This function increase sequence number after packetization.
 	/// @param payload RTP payload
 	/// @param payload RTP payload
 	/// @param setMark This needs to be `false` for all RTP packets with opus payload
 	/// @param setMark This needs to be `false` for all RTP packets with opus payload
-	message_ptr packetize(binary payload, bool setMark) override;
+	binary_ptr packetize(binary_ptr payload, bool setMark) override;
+
+	/// Creates RTP packet for given samples (all samples share same RTP timesamp)
+	/// @param messages opus samples
+	/// @param control RTCP
+	/// @returns RTP packets and unchanged `control`
+	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) override;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 17 - 106
include/rtc/peerconnection.hpp

@@ -23,42 +23,31 @@
 #include "configuration.hpp"
 #include "configuration.hpp"
 #include "datachannel.hpp"
 #include "datachannel.hpp"
 #include "description.hpp"
 #include "description.hpp"
-#include "include.hpp"
+#include "common.hpp"
 #include "init.hpp"
 #include "init.hpp"
 #include "message.hpp"
 #include "message.hpp"
 #include "reliability.hpp"
 #include "reliability.hpp"
-#include "rtc.hpp"
 #include "track.hpp"
 #include "track.hpp"
 
 
-#include <atomic>
 #include <chrono>
 #include <chrono>
 #include <functional>
 #include <functional>
-#include <future>
-#include <list>
-#include <mutex>
-#include <shared_mutex>
-#include <thread>
-#include <unordered_map>
 
 
 namespace rtc {
 namespace rtc {
 
 
-class Certificate;
-class Processor;
-class IceTransport;
-class DtlsTransport;
-class SctpTransport;
+namespace impl {
 
 
-using certificate_ptr = std::shared_ptr<Certificate>;
-using future_certificate_ptr = std::shared_future<certificate_ptr>;
+struct PeerConnection;
+
+}
 
 
 struct RTC_CPP_EXPORT DataChannelInit {
 struct RTC_CPP_EXPORT DataChannelInit {
 	Reliability reliability = {};
 	Reliability reliability = {};
 	bool negotiated = false;
 	bool negotiated = false;
-	std::optional<uint16_t> id = nullopt;
+	optional<uint16_t> id = nullopt;
 	string protocol = "";
 	string protocol = "";
 };
 };
 
 
-class RTC_CPP_EXPORT PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
+class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
 public:
 public:
 	enum class State : int {
 	enum class State : int {
 		New = RTC_NEW,
 		New = RTC_NEW,
@@ -84,7 +73,7 @@ public:
 	} rtcSignalingState;
 	} rtcSignalingState;
 
 
 	PeerConnection();
 	PeerConnection();
-	PeerConnection(const Configuration &config);
+	PeerConnection(Configuration config);
 	~PeerConnection();
 	~PeerConnection();
 
 
 	void close();
 	void close();
@@ -96,10 +85,10 @@ public:
 	bool hasLocalDescription() const;
 	bool hasLocalDescription() const;
 	bool hasRemoteDescription() const;
 	bool hasRemoteDescription() const;
 	bool hasMedia() const;
 	bool hasMedia() const;
-	std::optional<Description> localDescription() const;
-	std::optional<Description> remoteDescription() const;
-	std::optional<string> localAddress() const;
-	std::optional<string> remoteAddress() const;
+	optional<Description> localDescription() const;
+	optional<Description> remoteDescription() const;
+	optional<string> localAddress() const;
+	optional<string> remoteAddress() const;
 	bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
 	bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
 
 
 	void setLocalDescription(Description::Type type = Description::Type::Unspec);
 	void setLocalDescription(Description::Type type = Description::Type::Unspec);
@@ -107,12 +96,12 @@ public:
 	void setRemoteDescription(Description description);
 	void setRemoteDescription(Description description);
 	void addRemoteCandidate(Candidate candidate);
 	void addRemoteCandidate(Candidate candidate);
 
 
-	std::shared_ptr<DataChannel> addDataChannel(string label, DataChannelInit init = {});
+	shared_ptr<DataChannel> createDataChannel(string label, DataChannelInit init = {});
+	void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
 
 
-	// Equivalent to calling addDataChannel() and setLocalDescription()
-	std::shared_ptr<DataChannel> createDataChannel(string label, DataChannelInit init = {});
+	shared_ptr<Track> addTrack(Description::Media description);
+	void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
 
 
-	void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
 	void onLocalDescription(std::function<void(Description description)> callback);
 	void onLocalDescription(std::function<void(Description description)> callback);
 	void onLocalCandidate(std::function<void(Candidate candidate)> callback);
 	void onLocalCandidate(std::function<void(Candidate candidate)> callback);
 	void onStateChange(std::function<void(State state)> callback);
 	void onStateChange(std::function<void(State state)> callback);
@@ -123,85 +112,7 @@ public:
 	void clearStats();
 	void clearStats();
 	size_t bytesSent();
 	size_t bytesSent();
 	size_t bytesReceived();
 	size_t bytesReceived();
-	std::optional<std::chrono::milliseconds> rtt();
-
-	// Track media support requires compiling with libSRTP
-	std::shared_ptr<Track> addTrack(Description::Media description);
-	void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
-
-private:
-	std::shared_ptr<IceTransport> initIceTransport();
-	std::shared_ptr<DtlsTransport> initDtlsTransport();
-	std::shared_ptr<SctpTransport> initSctpTransport();
-	void closeTransports();
-
-	void endLocalCandidates();
-	bool checkFingerprint(const std::string &fingerprint) const;
-	void forwardMessage(message_ptr message);
-	void forwardMedia(message_ptr message);
-	void forwardBufferedAmount(uint16_t stream, size_t amount);
-	std::optional<std::string> getMidFromSsrc(uint32_t ssrc);
-
-	std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
-	                                                DataChannelInit init);
-	std::shared_ptr<DataChannel> findDataChannel(uint16_t stream);
-	void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
-	void openDataChannels();
-	void closeDataChannels();
-	void remoteCloseDataChannels();
-
-	void incomingTrack(Description::Media description);
-	void openTracks();
-
-	void validateRemoteDescription(const Description &description);
-	void processLocalDescription(Description description);
-	void processLocalCandidate(Candidate candidate);
-	void processRemoteDescription(Description description);
-	void processRemoteCandidate(Candidate candidate);
-	string localBundleMid() const;
-
-	void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
-	void triggerTrack(std::shared_ptr<Track> track);
-	bool changeState(State state);
-	bool changeGatheringState(GatheringState state);
-	bool changeSignalingState(SignalingState state);
-
-	void resetCallbacks();
-
-	void outgoingMedia(message_ptr message);
-
-	const init_token mInitToken = Init::Token();
-	const Configuration mConfig;
-	const future_certificate_ptr mCertificate;
-	const std::unique_ptr<Processor> mProcessor;
-
-	std::optional<Description> mLocalDescription, mRemoteDescription;
-	std::optional<Description> mCurrentLocalDescription;
-	mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
-
-	std::shared_ptr<IceTransport> mIceTransport;
-	std::shared_ptr<DtlsTransport> mDtlsTransport;
-	std::shared_ptr<SctpTransport> mSctpTransport;
-
-	std::unordered_map<uint16_t, std::weak_ptr<DataChannel>> mDataChannels; // by stream ID
-	std::unordered_map<string, std::weak_ptr<Track>> mTracks;               // by mid
-	std::vector<std::weak_ptr<Track>> mTrackLines;                          // by SDP order
-	std::shared_mutex mDataChannelsMutex, mTracksMutex;
-
-	std::unordered_map<uint32_t, string> mMidFromSsrc; // cache
-
-	std::atomic<State> mState;
-	std::atomic<GatheringState> mGatheringState;
-	std::atomic<SignalingState> mSignalingState;
-	std::atomic<bool> mNegotiationNeeded;
-
-	synchronized_callback<std::shared_ptr<DataChannel>> mDataChannelCallback;
-	synchronized_callback<Description> mLocalDescriptionCallback;
-	synchronized_callback<Candidate> mLocalCandidateCallback;
-	synchronized_callback<State> mStateChangeCallback;
-	synchronized_callback<GatheringState> mGatheringStateChangeCallback;
-	synchronized_callback<SignalingState> mSignalingStateChangeCallback;
-	synchronized_callback<std::shared_ptr<Track>> mTrackCallback;
+	optional<std::chrono::milliseconds> rtt();
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 2 - 3
include/rtc/reliability.hpp

@@ -19,10 +19,9 @@
 #ifndef RTC_RELIABILITY_H
 #ifndef RTC_RELIABILITY_H
 #define RTC_RELIABILITY_H
 #define RTC_RELIABILITY_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 #include <chrono>
 #include <chrono>
-#include <variant>
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -31,7 +30,7 @@ struct Reliability {
 
 
 	Type type = Type::Reliable;
 	Type type = Type::Reliable;
 	bool unordered = false;
 	bool unordered = false;
-	std::variant<int, std::chrono::milliseconds> rexmit = 0;
+	variant<int, std::chrono::milliseconds> rexmit = 0;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 37 - 20
include/rtc/rtc.h

@@ -39,8 +39,15 @@ extern "C" {
 #define RTC_ENABLE_WEBSOCKET 1
 #define RTC_ENABLE_WEBSOCKET 1
 #endif
 #endif
 
 
+#ifndef RTC_ENABLE_MEDIA
+#define RTC_ENABLE_MEDIA 1
+#endif
+
+#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)1400)
+#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)
 #endif
 #endif
 
 
 #include <stdbool.h>
 #include <stdbool.h>
@@ -85,20 +92,20 @@ typedef enum { // Don't change, it must match plog severity
 
 
 typedef enum {
 typedef enum {
     // video
     // video
-    RTC_CODEC_H264,
-    RTC_CODEC_VP8,
-    RTC_CODEC_VP9,
+    RTC_CODEC_H264 = 0,
+    RTC_CODEC_VP8 = 1,
+    RTC_CODEC_VP9 = 2,
 
 
     // audio
     // audio
-    RTC_CODEC_OPUS
+    RTC_CODEC_OPUS = 128
 } rtcCodec;
 } rtcCodec;
 
 
 typedef enum {
 typedef enum {
-    RTC_DIRECTION_UNKNOWN,
-    RTC_DIRECTION_SENDONLY,
-    RTC_DIRECTION_RECVONLY,
-    RTC_DIRECTION_SENDRECV,
-    RTC_DIRECTION_INACTIVE
+    RTC_DIRECTION_UNKNOWN = 0,
+    RTC_DIRECTION_SENDONLY = 1,
+    RTC_DIRECTION_RECVONLY = 2,
+    RTC_DIRECTION_SENDRECV = 3,
+    RTC_DIRECTION_INACTIVE = 4
 } rtcDirection;
 } rtcDirection;
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA
@@ -113,15 +120,17 @@ typedef struct {
 	const char **iceServers;
 	const char **iceServers;
 	int iceServersCount;
 	int iceServersCount;
 	bool enableIceTcp;
 	bool enableIceTcp;
+	bool disableAutoNegotiation;
 	uint16_t portRangeBegin;
 	uint16_t portRangeBegin;
 	uint16_t portRangeEnd;
 	uint16_t portRangeEnd;
+	int mtu; // <= 0 means automatic
 } rtcConfiguration;
 } rtcConfiguration;
 
 
 typedef struct {
 typedef struct {
 	bool unordered;
 	bool unordered;
 	bool unreliable;
 	bool unreliable;
-	unsigned int maxPacketLifeTime; // ignored if reliable
-	unsigned int maxRetransmits;    // ignored if reliable
+	int maxPacketLifeTime; // ignored if reliable
+	int maxRetransmits;    // ignored if reliable
 } rtcReliability;
 } rtcReliability;
 
 
 typedef struct {
 typedef struct {
@@ -174,6 +183,9 @@ RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
 RTC_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
 
 
+RTC_EXPORT int rtcGetLocalDescriptionType(int pc, char *buffer, int size);
+RTC_EXPORT int rtcGetRemoteDescriptionType(int pc, char *buffer, int size);
+
 RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 
 
@@ -182,10 +194,6 @@ RTC_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, c
 
 
 // DataChannel
 // DataChannel
 RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
 RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
-RTC_EXPORT int rtcAddDataChannel(int pc, const char *label); // returns dc id
-RTC_EXPORT int rtcAddDataChannelEx(int pc, const char *label,
-                                   const rtcDataChannelInit *init); // returns dc id
-// Equivalent to calling rtcAddDataChannel() and rtcSetLocalDescription()
 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,
                                       const rtcDataChannelInit *init); // returns dc id
                                       const rtcDataChannelInit *init); // returns dc id
@@ -215,8 +223,9 @@ RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
 /// @param _direction Direction
 /// @param _direction Direction
 /// @param _name Name (optional)
 /// @param _name Name (optional)
 /// @param _msid MSID (optional)
 /// @param _msid MSID (optional)
+/// @param _trackID Track ID used in MSID (optional)
 /// @returns Track id
 /// @returns Track id
-RTC_EXPORT int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection direction, const char *_name, const char *_msid);
+RTC_EXPORT int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection direction, const char *_name, const char *_msid, const char *_trackID);
 
 
 /// Set H264PacketizationHandler for track
 /// Set H264PacketizationHandler for track
 /// @param tr Track id
 /// @param tr Track id
@@ -239,6 +248,15 @@ RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char
 /// @param _timestamp Timestamp
 /// @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);
 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
+/// @param tr Track id
+int rtcChainRtcpSrReporter(int tr);
+
+/// Chain RtcpNackResponder to handler chain for given track
+/// @param tr Track id
+/// @param maxStoredPacketsCount Maximum stored packet count
+int rtcChainRtcpNackResponder(int tr, unsigned maxStoredPacketsCount);
+
 /// Set start time for RTP stream
 /// Set start time for RTP stream
 /// @param startTime_s Start time in seconds
 /// @param startTime_s Start time in seconds
 /// @param timeIntervalSince1970 Set true if `startTime_s` is time interval since 1970, false if `startTime_s` is time interval since 1900
 /// @param timeIntervalSince1970 Set true if `startTime_s` is time interval since 1970, false if `startTime_s` is time interval since 1900
@@ -249,7 +267,6 @@ int rtcSetRtpConfigurationStartTime(int id, double startTime_s, bool timeInterva
 /// @param id Track identifier
 /// @param id Track identifier
 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
 /// @param id Track id
 /// @param seconds Seconds
 /// @param seconds Seconds
@@ -282,9 +299,9 @@ int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp);
 /// @param timestamp Pointer for result
 /// @param timestamp Pointer for result
 int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t * timestamp);
 int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t * timestamp);
 
 
-/// Set `NeedsToReport` flag in RtcpSenderReporter handler identified by given track id
+/// Set `NeedsToReport` flag in RtcpSrReporter handler identified by given track id
 /// @param id Track id
 /// @param id Track id
-int rtcSetNeedsToSendRTCPSR(int id);
+int rtcSetNeedsToSendRtcpSr(int id);
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA
 
 

+ 15 - 4
include/rtc/rtc.hpp

@@ -16,19 +16,32 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
  */
 
 
+// C API
+#include "rtc.h"
+
 // C++ API
 // C++ API
-#include "include.hpp"
+#include "common.hpp"
 #include "init.hpp" // for rtc::Cleanup()
 #include "init.hpp" // for rtc::Cleanup()
 #include "log.hpp"
 #include "log.hpp"
 //
 //
 #include "datachannel.hpp"
 #include "datachannel.hpp"
+#include "track.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
+
+#if RTC_ENABLE_WEBSOCKET
+
+// WebSocket
 #include "websocket.hpp"
 #include "websocket.hpp"
 
 
+#endif // RTC_ENABLE_WEBSOCKET
+
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-// RTCP handling
+// Media handling
 #include "rtcpreceivingsession.hpp"
 #include "rtcpreceivingsession.hpp"
+#include "mediachainablehandler.hpp"
+#include "rtcpsrreporter.hpp"
+#include "rtcpnackresponder.hpp"
 
 
 // Opus/h264 streaming
 // Opus/h264 streaming
 #include "h264packetizationhandler.hpp"
 #include "h264packetizationhandler.hpp"
@@ -36,5 +49,3 @@
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA
 
 
-// C API
-#include "rtc.h"

+ 94 - 0
include/rtc/rtcpnackresponder.hpp

@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTC_RTCP_NACK_RESPONDER_H
+#define RTC_RTCP_NACK_RESPONDER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandlerelement.hpp"
+
+#include <unordered_map>
+#include <queue>
+
+namespace rtc {
+
+class RTC_CPP_EXPORT RtcpNackResponder: public MediaHandlerElement {
+
+	/// Packet storage
+	class RTC_CPP_EXPORT Storage {
+		
+		/// Packet storage element
+		struct RTC_CPP_EXPORT Element {
+			Element(binary_ptr packet, uint16_t sequenceNumber, shared_ptr<Element> next = nullptr);
+			const binary_ptr packet;
+			const uint16_t sequenceNumber;
+			/// Pointer to newer element
+			shared_ptr<Element> next = nullptr;
+		};
+
+	private:
+		/// Oldest packet in storage
+		shared_ptr<Element> oldest = nullptr;
+		/// Newest packet in storage
+		shared_ptr<Element> newest = nullptr;
+
+		/// Inner storage
+		std::unordered_map<uint16_t, shared_ptr<Element>> storage{};
+
+		/// Maximum storage size
+		const unsigned maximumSize;
+
+		/// Returnst current size
+		unsigned size();
+
+	public:
+		static const unsigned defaultMaximumSize = 512;
+
+		Storage(unsigned _maximumSize);
+
+		/// Returns packet with given sequence number
+		optional<binary_ptr> get(uint16_t sequenceNumber);
+
+		/// Stores packet
+		/// @param packet Packet
+		void store(binary_ptr packet);
+	};
+
+	const shared_ptr<Storage> storage;
+	std::mutex reportMutex;
+
+public:
+	RtcpNackResponder(unsigned maxStoredPacketCount = Storage::defaultMaximumSize);
+
+	/// Checks for RTCP NACK and handles it,
+	/// @param message RTCP message
+	/// @returns unchanged RTCP message and requested RTP packets
+	ChainedIncomingControlProduct processIncomingControlMessage(message_ptr message) override;
+
+	/// Stores RTP packets in internal storage
+	/// @param messages RTP packets
+	/// @param control RTCP
+	/// @returns Unchanged RTP and RTCP
+	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) override;
+};
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_RTCP_NACK_RESPONDER_H */

+ 3 - 3
include/rtc/rtcpreceivingsession.hpp

@@ -22,15 +22,15 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-#include "include.hpp"
-#include "rtcphandler.hpp"
+#include "common.hpp"
+#include "mediahandler.hpp"
 #include "message.hpp"
 #include "message.hpp"
 #include "rtp.hpp"
 #include "rtp.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
 // An RtcpSession can be plugged into a Track to handle the whole RTCP session
 // An RtcpSession can be plugged into a Track to handle the whole RTCP session
-class RTC_CPP_EXPORT RtcpReceivingSession : public RtcpHandler {
+class RTC_CPP_EXPORT RtcpReceivingSession : public MediaHandler {
 public:
 public:
 	message_ptr incoming(message_ptr ptr) override;
 	message_ptr incoming(message_ptr ptr) override;
 	message_ptr outgoing(message_ptr ptr) override;
 	message_ptr outgoing(message_ptr ptr) override;

+ 8 - 31
include/rtc/rtcpsenderreporter.hpp → include/rtc/rtcpsrreporter.hpp

@@ -1,5 +1,4 @@
-/*
- * 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 program is free software; you can redistribute it and/or
@@ -23,11 +22,12 @@
 
 
 #include "message.hpp"
 #include "message.hpp"
 #include "rtppacketizationconfig.hpp"
 #include "rtppacketizationconfig.hpp"
+#include "mediahandlerelement.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-/// Class for sending RTCP SR
-class RTC_CPP_EXPORT RtcpSenderReporter {
+class RTC_CPP_EXPORT RtcpSrReporter: public MediaHandlerElement {
+
 	bool needsToReport = false;
 	bool needsToReport = false;
 
 
 	uint32_t packetCount = 0;
 	uint32_t packetCount = 0;
@@ -39,10 +39,6 @@ class RTC_CPP_EXPORT RtcpSenderReporter {
 	void addToReport(RTP *rtp, uint32_t rtpSize);
 	void addToReport(RTP *rtp, uint32_t rtpSize);
 	message_ptr getSenderReport(uint32_t timestamp);
 	message_ptr getSenderReport(uint32_t timestamp);
 
 
-protected:
-	/// Outgoing callback for sender reports
-	synchronized_callback<message_ptr> senderReportOutgoingCallback;
-
 public:
 public:
 	static uint64_t secondsToNTP(double seconds);
 	static uint64_t secondsToNTP(double seconds);
 
 
@@ -50,9 +46,11 @@ public:
 	const uint32_t &previousReportedTimestamp = _previousReportedTimestamp;
 	const uint32_t &previousReportedTimestamp = _previousReportedTimestamp;
 
 
 	/// RTP configuration
 	/// RTP configuration
-	const std::shared_ptr<RtpPacketizationConfig> rtpConfig;
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
 
 
-	RtcpSenderReporter(std::shared_ptr<RtpPacketizationConfig> rtpConfig);
+	RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig);
+
+	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) override;
 
 
 	/// Set `needsToReport` flag. Sender report will be sent before next RTP packet with same
 	/// Set `needsToReport` flag. Sender report will be sent before next RTP packet with same
 	/// timestamp.
 	/// timestamp.
@@ -63,27 +61,6 @@ public:
 	/// @note `time_offset = rtpConfig->startTime_s -
 	/// @note `time_offset = rtpConfig->startTime_s -
 	/// rtpConfig->timestampToSeconds(rtpConfig->timestamp)`
 	/// rtpConfig->timestampToSeconds(rtpConfig->timestamp)`
 	void startRecording();
 	void startRecording();
-
-	/// Send RTCP SR with given timestamp
-	/// @param timestamp timestamp of the RTCP SR
-	void sendReport(uint32_t timestamp);
-
-protected:
-	/// Calls given block with function for statistics. Sends RTCP SR packet with current timestamp
-	/// before `block` call if `needs_to_report` flag is true.
-	/// @param block Block of code to run. This block has function for rtp stats recording.
-	template <typename T>
-	T withStatsRecording(std::function<T(std::function<void(message_ptr)>)> block) {
-		if (needsToReport) {
-			sendReport(rtpConfig->timestamp);
-			needsToReport = false;
-		}
-		auto result = block([this](message_ptr _rtp) {
-			auto rtp = reinterpret_cast<RTP *>(_rtp->data());
-			this->addToReport(rtp, _rtp->size());
-		});
-		return result;
-	}
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 41 - 15
include/rtc/rtp.hpp

@@ -298,13 +298,16 @@ public:
 
 
 private:
 private:
 	uint8_t _length;
 	uint8_t _length;
-	char _text;
+	char _text[1];
 
 
 public:
 public:
-	inline std::string text() const { return std::string(&_text, _length); }
+	inline std::string text() const { return std::string(_text, _length); }
 	inline void setText(std::string text) {
 	inline void setText(std::string text) {
-		_length = text.length();
-		memcpy(&_text, text.data(), _length);
+		if(text.size() > 0xFF)
+			throw std::invalid_argument("text is too long");
+
+		_length = uint8_t(text.size());
+		memcpy(_text, text.data(), text.size());
 	}
 	}
 
 
 	inline uint8_t length() { return _length; }
 	inline uint8_t length() { return _length; }
@@ -334,12 +337,12 @@ public:
 		return reinterpret_cast<RTCP_SDES_ITEM *>(base);
 		return reinterpret_cast<RTCP_SDES_ITEM *>(base);
 	}
 	}
 
 
-	long safelyCountChunkSize(unsigned int maxChunkSize) {
+	long safelyCountChunkSize(size_t maxChunkSize) {
 		if (maxChunkSize < RTCP_SDES_CHUNK::size({})) {
 		if (maxChunkSize < RTCP_SDES_CHUNK::size({})) {
 			// chunk is truncated
 			// chunk is truncated
 			return -1;
 			return -1;
 		} else {
 		} else {
-			unsigned int size = sizeof(SSRC);
+			size_t size = sizeof(SSRC);
 			unsigned int i = 0;
 			unsigned int i = 0;
 			// We can always access first 4 bytes of first item (in case of no items there will be 4
 			// We can always access first 4 bytes of first item (in case of no items there will be 4
 			// null bytes)
 			// null bytes)
@@ -407,7 +410,7 @@ public:
 			auto chunk = getChunk(i);
 			auto chunk = getChunk(i);
 			chunkSize += chunk->getSize();
 			chunkSize += chunk->getSize();
 		}
 		}
-		uint16_t length = (sizeof(header) + chunkSize) / 4 - 1;
+		uint16_t length = uint16_t((sizeof(header) + chunkSize) / 4 - 1);
 		header.prepareHeader(202, chunkCount, length);
 		header.prepareHeader(202, chunkCount, length);
 	}
 	}
 
 
@@ -617,8 +620,31 @@ struct RTCP_FIR {
 };
 };
 
 
 struct RTCP_NACK_PART {
 struct RTCP_NACK_PART {
-	uint16_t pid;
-	uint16_t blp;
+	uint16_t _pid;
+	uint16_t _blp;
+
+	uint16_t getPID() { return ntohs(_pid); }
+	uint16_t getBLP() { return ntohs(_blp); }
+
+	void setPID(uint16_t pid) { _pid = htons(pid); }
+	void setBLP(uint16_t blp) { _blp = htons(blp); }
+
+	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 {
 class RTCP_NACK {
@@ -644,16 +670,16 @@ public:
 	 */
 	 */
 	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)) {
 		if (*fciCount == 0 || missingPacket < *fciPID || missingPacket > (*fciPID + 16)) {
-			parts[*fciCount].pid = htons(missingPacket);
-			parts[*fciCount].blp = 0;
+			parts[*fciCount].setPID(missingPacket);
+			parts[*fciCount].setBLP(0);
 			*fciPID = missingPacket;
 			*fciPID = missingPacket;
 			(*fciCount)++;
 			(*fciCount)++;
 			return true;
 			return true;
 		} else {
 		} else {
-			// TODO SPEEED!
-			auto blp = ntohs(parts[(*fciCount) - 1].blp);
-			auto newBit = 1u << (unsigned int)(missingPacket - (1 + *fciPID));
-			parts[(*fciCount) - 1].blp = htons(blp | newBit);
+			// 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;
 			return false;
 		}
 		}
 	}
 	}

+ 3 - 3
include/rtc/rtppacketizationconfig.hpp

@@ -55,7 +55,7 @@ public:
 	/// @param startTimestamp Corresponding timestamp for given start time (current timestamp will
 	/// @param startTimestamp Corresponding timestamp for given start time (current timestamp will
 	/// be used if value is nullopt)
 	/// be used if value is nullopt)
 	void setStartTime(double startTime_s, EpochStart epochStart,
 	void setStartTime(double startTime_s, EpochStart epochStart,
-	                  std::optional<uint32_t> startTimestamp = std::nullopt);
+	                  optional<uint32_t> startTimestamp = std::nullopt);
 
 
 	/// Construct RTP configuration used in packetization process
 	/// Construct RTP configuration used in packetization process
 	/// @param ssrc SSRC of source
 	/// @param ssrc SSRC of source
@@ -66,8 +66,8 @@ public:
 	/// nullopt)
 	/// nullopt)
 	/// @param timestamp Initial timastamp of RTP packets (random number is choosed if nullopt)
 	/// @param timestamp Initial timastamp of RTP packets (random number is choosed if nullopt)
 	RtpPacketizationConfig(SSRC ssrc, std::string cname, uint8_t payloadType, uint32_t clockRate,
 	RtpPacketizationConfig(SSRC ssrc, std::string cname, uint8_t payloadType, uint32_t clockRate,
-	                       std::optional<uint16_t> sequenceNumber = std::nullopt,
-	                       std::optional<uint32_t> timestamp = std::nullopt);
+	                       optional<uint16_t> sequenceNumber = std::nullopt,
+	                       optional<uint32_t> timestamp = std::nullopt);
 
 
 	/// Convert timestamp to seconds
 	/// Convert timestamp to seconds
 	/// @param timestamp Timestamp
 	/// @param timestamp Timestamp

+ 3 - 3
include/rtc/rtppacketizer.hpp

@@ -32,19 +32,19 @@ class RTC_CPP_EXPORT RtpPacketizer {
 
 
 public:
 public:
 	// RTP configuration
 	// RTP configuration
-	const std::shared_ptr<RtpPacketizationConfig> rtpConfig;
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
 
 
 	/// Constructs packetizer with given RTP configuration.
 	/// Constructs 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
 	/// properties such as sequence number.
 	/// properties such as sequence number.
 	/// @param rtpConfig  RTP configuration
 	/// @param rtpConfig  RTP configuration
-	RtpPacketizer(std::shared_ptr<RtpPacketizationConfig> rtpConfig);
+	RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig);
 
 
 	/// Creates RTP packet for given payload based on `rtpConfig`.
 	/// Creates RTP packet for given payload based on `rtpConfig`.
 	/// @note This function increase sequence number after packetization.
 	/// @note This function increase sequence number after packetization.
 	/// @param payload RTP payload
 	/// @param payload RTP payload
 	/// @param setMark Set marker flag in RTP packet if true
 	/// @param setMark Set marker flag in RTP packet if true
-	virtual message_ptr packetize(binary payload, bool setMark);
+	virtual shared_ptr<binary> packetize(shared_ptr<binary> payload, bool setMark);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 17 - 34
include/rtc/track.hpp

@@ -20,28 +20,29 @@
 #define RTC_TRACK_H
 #define RTC_TRACK_H
 
 
 #include "channel.hpp"
 #include "channel.hpp"
+#include "common.hpp"
 #include "description.hpp"
 #include "description.hpp"
-#include "include.hpp"
+#include "mediahandler.hpp"
 #include "message.hpp"
 #include "message.hpp"
-#include "queue.hpp"
-#include "rtcphandler.hpp"
 
 
 #include <atomic>
 #include <atomic>
-#include <variant>
 #include <shared_mutex>
 #include <shared_mutex>
 
 
 namespace rtc {
 namespace rtc {
 
 
-#if RTC_ENABLE_MEDIA
-class DtlsSrtpTransport;
-#endif
+namespace impl {
+
+class Track;
+
+} // namespace impl
 
 
-class RTC_CPP_EXPORT Track final : public std::enable_shared_from_this<Track>, public Channel {
+class RTC_CPP_EXPORT Track final : private CheshireCat<impl::Track>, public Channel {
 public:
 public:
-	Track(Description::Media description);
+	Track(impl_ptr<impl::Track> impl);
 	~Track() = default;
 	~Track() = default;
 
 
 	string mid() const;
 	string mid() const;
+	Description::Direction direction() const;
 	Description::Media description() const;
 	Description::Media description() const;
 
 
 	void setDescription(Description::Media description);
 	void setDescription(Description::Media description);
@@ -54,35 +55,17 @@ public:
 	bool isClosed(void) const override;
 	bool isClosed(void) const override;
 	size_t maxMessageSize() const override;
 	size_t maxMessageSize() const override;
 
 
-	// Extended API
-	size_t availableAmount() const override;
-	std::optional<message_variant> receive() override;
-	std::optional<message_variant> peek() override;
-
 	bool requestKeyframe();
 	bool requestKeyframe();
 
 
-	// RTCP handler
-	void setRtcpHandler(std::shared_ptr<RtcpHandler> handler);
-	std::shared_ptr<RtcpHandler> getRtcpHandler();
+	void setMediaHandler(shared_ptr<MediaHandler> handler);
+	shared_ptr<MediaHandler> getMediaHandler();
 
 
-private:
-#if RTC_ENABLE_MEDIA
-	void open(std::shared_ptr<DtlsSrtpTransport> transport);
-	std::weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
-#endif
+	// Deprecated, use setMediaHandler() and getMediaHandler()
+	inline void setRtcpHandler(shared_ptr<MediaHandler> handler) { setMediaHandler(handler); }
+	inline shared_ptr<MediaHandler> getRtcpHandler() { return getMediaHandler(); }
 
 
-	void incoming(message_ptr message);
-	bool outgoing(message_ptr message);
-
-	Description::Media mMediaDescription;
-	std::atomic<bool> mIsClosed = false;
-
-	Queue<message_ptr> mRecvQueue;
-
-	std::shared_mutex mRtcpHandlerMutex;
-	std::shared_ptr<RtcpHandler> mRtcpHandler;
-
-	friend class PeerConnection;
+private:
+	using CheshireCat<impl::Track>::impl;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 33 - 56
include/rtc/include.hpp → include/rtc/utils.hpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2019-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,66 +16,15 @@
  * 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_INCLUDE_H
-#define RTC_INCLUDE_H
+#ifndef RTC_UTILS_H
+#define RTC_UTILS_H
 
 
-#ifndef RTC_ENABLE_MEDIA
-#define RTC_ENABLE_MEDIA 1
-#endif
-
-#ifndef RTC_ENABLE_WEBSOCKET
-#define RTC_ENABLE_WEBSOCKET 1
-#endif
-
-#ifdef _WIN32
-#define RTC_CPP_EXPORT __declspec(dllexport)
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0602 // Windows 8
-#endif
-#ifdef _MSC_VER
-#pragma warning(disable : 4251) // disable "X needs to have dll-interface..."
-#endif
-#else
-#define RTC_CPP_EXPORT
-#endif
-
-#include "log.hpp"
-
-#include <cstddef>
 #include <functional>
 #include <functional>
 #include <memory>
 #include <memory>
 #include <mutex>
 #include <mutex>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
 
 
 namespace rtc {
 namespace rtc {
 
 
-using std::byte;
-using std::string;
-using std::string_view;
-using binary = std::vector<byte>;
-
-using std::nullopt;
-
-using std::size_t;
-using std::uint16_t;
-using std::uint32_t;
-using std::uint64_t;
-using std::uint8_t;
-
-const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length
-const size_t MAX_NUMERICSERV_LEN = 6;  // Max port string representation length
-
-const uint16_t DEFAULT_SCTP_PORT = 5000;          // SCTP port to use by default
-const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536;    // Remote max message size if not specified in SDP
-const size_t LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Local max message size
-
-const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
-
-const int THREADPOOL_SIZE = 4; // Number of threads in the global thread pool
-
 // overloaded helper
 // overloaded helper
 template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
 template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
 template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
 template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
@@ -92,7 +41,7 @@ template <typename F, typename T, typename... Args> auto weak_bind(F &&f, T *t,
 }
 }
 
 
 // scope_guard helper
 // scope_guard helper
-class scope_guard {
+class scope_guard final {
 public:
 public:
 	scope_guard(std::function<void()> func) : function(std::move(func)) {}
 	scope_guard(std::function<void()> func) : function(std::move(func)) {}
 	scope_guard(scope_guard &&other) = delete;
 	scope_guard(scope_guard &&other) = delete;
@@ -108,7 +57,8 @@ private:
 	std::function<void()> function;
 	std::function<void()> function;
 };
 };
 
 
-template <typename... Args> class synchronized_callback {
+// callback with built-in synchronization
+template <typename... Args> class synchronized_callback final {
 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); }
@@ -154,6 +104,33 @@ private:
 	std::function<void(Args...)> callback;
 	std::function<void(Args...)> callback;
 	mutable std::recursive_mutex mutex;
 	mutable std::recursive_mutex mutex;
 };
 };
+
+// pimpl base class
+template <typename T> using impl_ptr = std::shared_ptr<T>;
+template <typename T> class CheshireCat {
+public:
+	CheshireCat(impl_ptr<T> impl) : mImpl(std::move(impl)) {}
+	template <typename... Args>
+	CheshireCat(Args... args) : mImpl(std::make_shared<T>(std::move(args)...)) {}
+	CheshireCat(CheshireCat<T> &&cc) { *this = std::move(cc); }
+	CheshireCat(const CheshireCat<T> &) = delete;
+
+	virtual ~CheshireCat() = default;
+
+	CheshireCat &operator=(CheshireCat<T> &&cc) {
+		mImpl = std::move(cc.mImpl);
+		return *this;
+	};
+	CheshireCat &operator=(const CheshireCat<T> &) = delete;
+
+protected:
+	impl_ptr<T> impl() { return mImpl; }
+	impl_ptr<const T> impl() const { return mImpl; }
+
+private:
+	impl_ptr<T> mImpl;
+};
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif

+ 16 - 46
include/rtc/websocket.hpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2020 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
@@ -22,24 +22,18 @@
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
 #include "channel.hpp"
 #include "channel.hpp"
-#include "include.hpp"
-#include "init.hpp"
+#include "common.hpp"
 #include "message.hpp"
 #include "message.hpp"
-#include "queue.hpp"
-
-#include <atomic>
-#include <optional>
-#include <thread>
-#include <variant>
 
 
 namespace rtc {
 namespace rtc {
 
 
-class TcpTransport;
-class TlsTransport;
-class WsTransport;
+namespace impl {
+
+struct WebSocket;
+
+}
 
 
-class RTC_CPP_EXPORT WebSocket final : public Channel,
-                                       public std::enable_shared_from_this<WebSocket> {
+class RTC_CPP_EXPORT WebSocket final : private CheshireCat<impl::WebSocket>, public Channel {
 public:
 public:
 	enum class State : int {
 	enum class State : int {
 		Connecting = 0,
 		Connecting = 0,
@@ -53,49 +47,25 @@ public:
 		std::vector<string> protocols;
 		std::vector<string> protocols;
 	};
 	};
 
 
-	WebSocket(std::optional<Configuration> config = nullopt);
+	WebSocket();
+	WebSocket(Configuration config);
 	~WebSocket();
 	~WebSocket();
 
 
 	State readyState() const;
 	State readyState() const;
 
 
-	void open(const string &url);
-	void close() override;
-	bool send(const message_variant data) override;
-	bool send(const byte *data, size_t size) override;
-
 	bool isOpen() const override;
 	bool isOpen() const override;
 	bool isClosed() const override;
 	bool isClosed() const override;
 	size_t maxMessageSize() const override;
 	size_t maxMessageSize() const override;
 
 
-	// Extended API
-	std::optional<message_variant> receive() override;
-	std::optional<message_variant> peek() override;
-	size_t availableAmount() const override; // total size available to receive
+	void open(const string &url);
+	void close() override;
+	bool send(const message_variant data) override;
+	bool send(const byte *data, size_t size) override;
 
 
 private:
 private:
-	bool changeState(State state);
-	void remoteClose();
-	bool outgoing(message_ptr message);
-	void incoming(message_ptr message);
-
-	std::shared_ptr<TcpTransport> initTcpTransport();
-	std::shared_ptr<TlsTransport> initTlsTransport();
-	std::shared_ptr<WsTransport> initWsTransport();
-	void closeTransports();
-
-	init_token mInitToken = Init::Token();
-
-	std::shared_ptr<TcpTransport> mTcpTransport;
-	std::shared_ptr<TlsTransport> mTlsTransport;
-	std::shared_ptr<WsTransport> mWsTransport;
-	std::recursive_mutex mInitMutex;
-
-	const Configuration mConfig;
-	string mScheme, mHost, mHostname, mService, mPath;
-	std::atomic<State> mState = State::Closed;
-
-	Queue<message_ptr> mRecvQueue;
+	using CheshireCat<impl::WebSocket>::impl;
 };
 };
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif

+ 15 - 9
src/candidate.cpp

@@ -17,6 +17,7 @@
  */
  */
 
 
 #include "candidate.hpp"
 #include "candidate.hpp"
+#include "globals.hpp"
 
 
 #include <algorithm>
 #include <algorithm>
 #include <array>
 #include <array>
@@ -99,7 +100,7 @@ void Candidate::parse(string candidate) {
 
 
 	// See RFC 8445 for format
 	// See RFC 8445 for format
 	std::istringstream iss(candidate);
 	std::istringstream iss(candidate);
-	string transport, typ_, type;
+	string typ_;
 	if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
 	if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
 	      iss >> mNode >> mService >> typ_ >> mTypeString && typ_ == "typ"))
 	      iss >> mNode >> mService >> typ_ >> mTypeString && typ_ == "typ"))
 		throw std::invalid_argument("Invalid candidate format");
 		throw std::invalid_argument("Invalid candidate format");
@@ -108,14 +109,14 @@ void Candidate::parse(string candidate) {
 	trim_begin(mTail);
 	trim_begin(mTail);
 	trim_end(mTail);
 	trim_end(mTail);
 
 
-	if (auto it = TypeMap.find(type); it != TypeMap.end())
+	if (auto it = TypeMap.find(mTypeString); it != TypeMap.end())
 		mType = it->second;
 		mType = it->second;
 	else
 	else
 		mType = Type::Unknown;
 		mType = Type::Unknown;
 
 
-	if (transport == "UDP" || transport == "udp") {
+	if (mTransportString == "UDP" || mTransportString == "udp") {
 		mTransportType = TransportType::Udp;
 		mTransportType = TransportType::Udp;
-	} else if (transport == "TCP" || transport == "tcp") {
+	} else if (mTransportString == "TCP" || mTransportString == "tcp") {
 		// Peek tail to find TCP type
 		// Peek tail to find TCP type
 		std::istringstream tiss(mTail);
 		std::istringstream tiss(mTail);
 		string tcptype_, tcptype;
 		string tcptype_, tcptype;
@@ -167,9 +168,12 @@ bool Candidate::resolve(ResolveMode mode) {
 				if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
 				if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
 				                MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
 				                MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
-
+					try {
+						mPort = uint16_t(std::stoul(servbuffer));
+					} catch (...) {
+						return false;
+					}
 					mAddress = nodebuffer;
 					mAddress = nodebuffer;
-					mPort = uint16_t(std::stoul(servbuffer));
 					mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
 					mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
 					PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
 					PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
 					break;
 					break;
@@ -216,7 +220,9 @@ Candidate::operator string() const {
 }
 }
 
 
 bool Candidate::operator==(const Candidate &other) const {
 bool Candidate::operator==(const Candidate &other) const {
-	return mFoundation == other.mFoundation;
+	return (mFoundation == other.mFoundation &&
+	        mService == other.mService &&
+	        mNode == other.mNode);
 }
 }
 
 
 bool Candidate::operator!=(const Candidate &other) const {
 bool Candidate::operator!=(const Candidate &other) const {
@@ -227,11 +233,11 @@ bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
 
 
 Candidate::Family Candidate::family() const { return mFamily; }
 Candidate::Family Candidate::family() const { return mFamily; }
 
 
-std::optional<string> Candidate::address() const {
+optional<string> Candidate::address() const {
 	return isResolved() ? std::make_optional(mAddress) : nullopt;
 	return isResolved() ? std::make_optional(mAddress) : nullopt;
 }
 }
 
 
-std::optional<uint16_t> Candidate::port() const {
+optional<uint16_t> Candidate::port() const {
 	return isResolved() ? std::make_optional(mPort) : nullopt;
 	return isResolved() ? std::make_optional(mPort) : nullopt;
 }
 }
 
 

+ 201 - 122
src/capi.cpp

@@ -16,16 +16,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
  */
 
 
-#include "include.hpp"
-
 #include "rtc.h"
 #include "rtc.h"
 
 
-#include "datachannel.hpp"
-#include "log.hpp"
-#include "peerconnection.hpp"
-#if RTC_ENABLE_WEBSOCKET
-#include "websocket.hpp"
-#endif
+#include "rtc.hpp"
 
 
 #include "plog/Formatters/FuncMessageFormatter.h"
 #include "plog/Formatters/FuncMessageFormatter.h"
 
 
@@ -42,9 +35,6 @@
 #endif
 #endif
 
 
 using namespace rtc;
 using namespace rtc;
-using std::optional;
-using std::shared_ptr;
-using std::string;
 using std::chrono::milliseconds;
 using std::chrono::milliseconds;
 
 
 namespace {
 namespace {
@@ -53,7 +43,8 @@ std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
 std::unordered_map<int, shared_ptr<Track>> trackMap;
 std::unordered_map<int, shared_ptr<Track>> trackMap;
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
-std::unordered_map<int, shared_ptr<RtcpSenderReporter>> rtcpSenderMap;
+std::unordered_map<int, shared_ptr<MediaChainableHandler>> rtcpChainableHandlerMap;
+std::unordered_map<int, shared_ptr<RtcpSrReporter>> rtcpSrReporterMap;
 std::unordered_map<int, shared_ptr<RtpPacketizationConfig>> rtpConfigMap;
 std::unordered_map<int, shared_ptr<RtpPacketizationConfig>> rtpConfigMap;
 #endif
 #endif
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
@@ -63,7 +54,7 @@ std::unordered_map<int, void *> userPointerMap;
 std::mutex mutex;
 std::mutex mutex;
 int lastId = 0;
 int lastId = 0;
 
 
-std::optional<void *> getUserPointer(int id) {
+optional<void *> getUserPointer(int id) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
 	auto it = userPointerMap.find(id);
 	auto it = userPointerMap.find(id);
 	return it != userPointerMap.end() ? std::make_optional(it->second) : nullopt;
 	return it != userPointerMap.end() ? std::make_optional(it->second) : nullopt;
@@ -141,7 +132,8 @@ void eraseTrack(int tr) {
 	if (trackMap.erase(tr) == 0)
 	if (trackMap.erase(tr) == 0)
 		throw std::invalid_argument("Track ID does not exist");
 		throw std::invalid_argument("Track ID does not exist");
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
-	rtcpSenderMap.erase(tr);
+	rtcpSrReporterMap.erase(tr);
+	rtcpChainableHandlerMap.erase(tr);
 	rtpConfigMap.erase(tr);
 	rtpConfigMap.erase(tr);
 #endif
 #endif
 	userPointerMap.erase(tr);
 	userPointerMap.erase(tr);
@@ -149,25 +141,41 @@ void eraseTrack(int tr) {
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-shared_ptr<RtcpSenderReporter> getRTCPSender(int id) {
+shared_ptr<RtcpSrReporter> getRtcpSrReporter(int id) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
-	if (auto it = rtcpSenderMap.find(id); it != rtcpSenderMap.end())
+	if (auto it = rtcpSrReporterMap.find(id); it != rtcpSrReporterMap.end()) {
 		return it->second;
 		return it->second;
-	else
-		throw std::invalid_argument("RtcpSenderReporter ID does not exist");
+	} else {
+		throw std::invalid_argument("RtcpSRReporter ID does not exist");
+	}
+}
+
+void emplaceRtcpSrReporter(shared_ptr<RtcpSrReporter> ptr, int tr) {
+	std::lock_guard lock(mutex);
+	rtcpSrReporterMap.emplace(std::make_pair(tr, ptr));
+}
+
+shared_ptr<MediaChainableHandler> getMediaChainableHandler(int id) {
+	std::lock_guard lock(mutex);
+	if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) {
+		return it->second;
+	} else {
+		throw std::invalid_argument("RtcpChainableHandler ID does not exist");
+	}
 }
 }
 
 
-void emplaceRTCPSender(shared_ptr<RtcpSenderReporter> ptr, int tr) {
+void emplaceMediaChainableHandler(shared_ptr<MediaChainableHandler> ptr, int tr) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
-	rtcpSenderMap.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("RTPConfiguration ID does not exist");
+	}
 }
 }
 
 
 void emplaceRTPConfig(shared_ptr<RtpPacketizationConfig> ptr, int tr) {
 void emplaceRTPConfig(shared_ptr<RtpPacketizationConfig> ptr, int tr) {
@@ -254,12 +262,6 @@ template <typename F> int wrap(F func) {
 	}
 	}
 }
 }
 
 
-#define WRAP(statement)                                                                            \
-	wrap([&]() {                                                                                   \
-		statement;                                                                                 \
-		return RTC_ERR_SUCCESS;                                                                    \
-	})
-
 int copyAndReturn(string s, char *buffer, int size) {
 int copyAndReturn(string s, char *buffer, int size) {
 	if (!buffer)
 	if (!buffer)
 		return int(s.size() + 1);
 		return int(s.size() + 1);
@@ -325,7 +327,7 @@ private:
 } // namespace
 } // namespace
 
 
 void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
 void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
-	static std::optional<plogAppender> appender;
+	static optional<plogAppender> appender;
 	const auto severity = static_cast<plog::Severity>(level);
 	const auto severity = static_cast<plog::Severity>(level);
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
 	if (appender) {
 	if (appender) {
@@ -344,23 +346,28 @@ void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
 void *rtcGetUserPointer(int i) { return getUserPointer(i).value_or(nullptr); }
 void *rtcGetUserPointer(int i) { return getUserPointer(i).value_or(nullptr); }
 
 
 int rtcCreatePeerConnection(const rtcConfiguration *config) {
 int rtcCreatePeerConnection(const rtcConfiguration *config) {
-	return WRAP({
+	return wrap([config] {
 		Configuration c;
 		Configuration c;
 		for (int i = 0; i < config->iceServersCount; ++i)
 		for (int i = 0; i < config->iceServersCount; ++i)
 			c.iceServers.emplace_back(string(config->iceServers[i]));
 			c.iceServers.emplace_back(string(config->iceServers[i]));
 
 
-		if (config->portRangeBegin || config->portRangeEnd) {
+		c.enableIceTcp = config->enableIceTcp;
+		c.disableAutoNegotiation = config->disableAutoNegotiation;
+
+		if (config->portRangeBegin > 0 || config->portRangeEnd > 0) {
 			c.portRangeBegin = config->portRangeBegin;
 			c.portRangeBegin = config->portRangeBegin;
 			c.portRangeEnd = config->portRangeEnd;
 			c.portRangeEnd = config->portRangeEnd;
 		}
 		}
 
 
-		c.enableIceTcp = config->enableIceTcp;
+		if(config->mtu > 0)
+			c.mtu = size_t(config->mtu);
+
 		return emplacePeerConnection(std::make_shared<PeerConnection>(c));
 		return emplacePeerConnection(std::make_shared<PeerConnection>(c));
 	});
 	});
 }
 }
 
 
 int rtcDeletePeerConnection(int pc) {
 int rtcDeletePeerConnection(int pc) {
-	return WRAP({
+	return wrap([pc] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		peerConnection->onDataChannel(nullptr);
 		peerConnection->onDataChannel(nullptr);
 		peerConnection->onTrack(nullptr);
 		peerConnection->onTrack(nullptr);
@@ -370,13 +377,16 @@ int rtcDeletePeerConnection(int pc) {
 		peerConnection->onGatheringStateChange(nullptr);
 		peerConnection->onGatheringStateChange(nullptr);
 
 
 		erasePeerConnection(pc);
 		erasePeerConnection(pc);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
-int rtcAddDataChannel(int pc, const char *label) { return rtcAddDataChannelEx(pc, label, nullptr); }
+int rtcCreateDataChannel(int pc, const char *label) {
+	return rtcCreateDataChannelEx(pc, label, nullptr);
+}
 
 
-int rtcAddDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
-	return WRAP({
+int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
+	return wrap([&] {
 		DataChannelInit dci = {};
 		DataChannelInit dci = {};
 		if (init) {
 		if (init) {
 			auto *reliability = &init->reliability;
 			auto *reliability = &init->reliability;
@@ -387,7 +397,7 @@ int rtcAddDataChannelEx(int pc, const char *label, const rtcDataChannelInit *ini
 					dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
 					dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
 				} else {
 				} else {
 					dci.reliability.type = Reliability::Type::Rexmit;
 					dci.reliability.type = Reliability::Type::Rexmit;
-					dci.reliability.rexmit = int(reliability->maxRetransmits);
+					dci.reliability.rexmit = reliability->maxRetransmits;
 				}
 				}
 			} else {
 			} else {
 				dci.reliability.type = Reliability::Type::Reliable;
 				dci.reliability.type = Reliability::Type::Reliable;
@@ -400,7 +410,7 @@ int rtcAddDataChannelEx(int pc, const char *label, const rtcDataChannelInit *ini
 
 
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		int dc = emplaceDataChannel(
 		int dc = emplaceDataChannel(
-		    peerConnection->addDataChannel(string(label ? label : ""), std::move(dci)));
+		    peerConnection->createDataChannel(string(label ? label : ""), std::move(dci)));
 
 
 		if (auto ptr = getUserPointer(pc))
 		if (auto ptr = getUserPointer(pc))
 			rtcSetUserPointer(dc, *ptr);
 			rtcSetUserPointer(dc, *ptr);
@@ -409,18 +419,8 @@ int rtcAddDataChannelEx(int pc, const char *label, const rtcDataChannelInit *ini
 	});
 	});
 }
 }
 
 
-int rtcCreateDataChannel(int pc, const char *label) {
-	return rtcCreateDataChannelEx(pc, label, nullptr);
-}
-
-int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
-	int dc = rtcAddDataChannelEx(pc, label, init);
-	rtcSetLocalDescription(pc, NULL);
-	return dc;
-}
-
 int rtcDeleteDataChannel(int dc) {
 int rtcDeleteDataChannel(int dc) {
-	return WRAP({
+	return wrap([dc] {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 		dataChannel->onOpen(nullptr);
 		dataChannel->onOpen(nullptr);
 		dataChannel->onClosed(nullptr);
 		dataChannel->onClosed(nullptr);
@@ -430,12 +430,14 @@ int rtcDeleteDataChannel(int dc) {
 		dataChannel->onAvailable(nullptr);
 		dataChannel->onAvailable(nullptr);
 
 
 		eraseDataChannel(dc);
 		eraseDataChannel(dc);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid) {
+void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid,
+             const char *_trackID) {
 
 
 	optional<string> name = nullopt;
 	optional<string> name = nullopt;
 	if (_name) {
 	if (_name) {
@@ -447,12 +449,18 @@ void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name,
 		msid = string(_msid);
 		msid = string(_msid);
 	}
 	}
 
 
-	description->addSSRC(ssrc, name, msid);
+	optional<string> trackID = nullopt;
+	if (_trackID) {
+		trackID = string(_trackID);
+	}
+
+	description->addSSRC(ssrc, name, msid, trackID);
 }
 }
 
 
 int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid,
 int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid,
-                  rtcDirection _direction, const char *_name, const char *_msid) {
-	return WRAP({
+                  rtcDirection _direction, const char *_name, const char *_msid,
+                  const char *_trackID) {
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		auto direction = rtcDirectionToDirection(_direction);
 		auto direction = rtcDirectionToDirection(_direction);
@@ -516,7 +524,7 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
 			throw std::invalid_argument("Unexpected codec");
 			throw std::invalid_argument("Unexpected codec");
 		} else {
 		} else {
 			auto description = optDescription.value();
 			auto description = optDescription.value();
-			setSSRC(&description, ssrc, _name, _msid);
+			setSSRC(&description, ssrc, _name, _msid, _trackID);
 
 
 			int tr = emplaceTrack(peerConnection->addTrack(std::move(description)));
 			int tr = emplaceTrack(peerConnection->addTrack(std::move(description)));
 			if (auto ptr = getUserPointer(pc)) {
 			if (auto ptr = getUserPointer(pc)) {
@@ -528,116 +536,146 @@ int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const
 }
 }
 
 
 int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char *cname, uint8_t payloadType,
 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) {
-	return WRAP({
+                                   uint32_t clockRate, uint16_t maxFragmentSize,
+                                   uint16_t sequenceNumber, uint32_t timestamp) {
+	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		// create RTP configuration
 		// create RTP configuration
 		auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
 		auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
 		                                              sequenceNumber, timestamp);
 		                                              sequenceNumber, timestamp);
 		// create packetizer
 		// create packetizer
-		auto packetizer = shared_ptr<H264RtpPacketizer>(new H264RtpPacketizer(rtpConfig));
-		// create H264 and RTCP SP handler
-		shared_ptr<H264PacketizationHandler> h264Handler(
-		    new H264PacketizationHandler(H264PacketizationHandler::Separator::Length, packetizer, maxFragmentSize));
-		emplaceRTCPSender(h264Handler, tr);
+		auto packetizer = std::make_shared<H264RtpPacketizer>(rtpConfig, maxFragmentSize);
+		// create H264 handler
+		auto h264Handler = std::make_shared<H264PacketizationHandler>(packetizer);
+		emplaceMediaChainableHandler(h264Handler, tr);
 		emplaceRTPConfig(rtpConfig, tr);
 		emplaceRTPConfig(rtpConfig, tr);
 		// set handler
 		// set handler
-		track->setRtcpHandler(h264Handler);
+		track->setMediaHandler(h264Handler);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetOpusPacketizationHandler(int tr, uint32_t ssrc, const char *cname, uint8_t payloadType,
 int rtcSetOpusPacketizationHandler(int tr, uint32_t ssrc, const char *cname, uint8_t payloadType,
                                    uint32_t clockRate, uint16_t sequenceNumber,
                                    uint32_t clockRate, uint16_t sequenceNumber,
                                    uint32_t timestamp) {
                                    uint32_t timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		// create RTP configuration
 		// create RTP configuration
 		auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
 		auto rtpConfig = getNewRtpPacketizationConfig(ssrc, cname, payloadType, clockRate,
 		                                              sequenceNumber, timestamp);
 		                                              sequenceNumber, timestamp);
 		// create packetizer
 		// create packetizer
-		auto packetizer = shared_ptr<OpusRtpPacketizer>(new OpusRtpPacketizer(rtpConfig));
-		// create Opus and RTCP SP handler
-		shared_ptr<OpusPacketizationHandler> opusHandler(new OpusPacketizationHandler(packetizer));
-		emplaceRTCPSender(opusHandler, tr);
+		auto packetizer = std::make_shared<OpusRtpPacketizer>(rtpConfig);
+		// create Opus handler
+		auto opusHandler = std::make_shared<OpusPacketizationHandler>(packetizer);
+		emplaceMediaChainableHandler(opusHandler, tr);
 		emplaceRTPConfig(rtpConfig, tr);
 		emplaceRTPConfig(rtpConfig, tr);
 		// set handler
 		// set handler
-		track->setRtcpHandler(opusHandler);
+		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,
 int rtcSetRtpConfigurationStartTime(int id, double startTime_s, bool timeIntervalSince1970,
                                     uint32_t timestamp) {
                                     uint32_t timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		auto epoch = RtpPacketizationConfig::EpochStart::T1900;
 		auto epoch = RtpPacketizationConfig::EpochStart::T1900;
 		if (timeIntervalSince1970) {
 		if (timeIntervalSince1970) {
 			epoch = RtpPacketizationConfig::EpochStart::T1970;
 			epoch = RtpPacketizationConfig::EpochStart::T1970;
 		}
 		}
 		config->setStartTime(startTime_s, epoch, timestamp);
 		config->setStartTime(startTime_s, epoch, timestamp);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcStartRtcpSenderReporterRecording(int id) {
 int rtcStartRtcpSenderReporterRecording(int id) {
-	return WRAP({
-		auto sender = getRTCPSender(id);
+	return wrap([id] {
+		auto sender = getRtcpSrReporter(id);
 		sender->startRecording();
 		sender->startRecording();
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp) {
 int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		*timestamp = config->secondsToTimestamp(seconds);
 		*timestamp = config->secondsToTimestamp(seconds);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds) {
 int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		*seconds = config->timestampToSeconds(timestamp);
 		*seconds = config->timestampToSeconds(timestamp);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp) {
 int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		*timestamp = config->timestamp;
 		*timestamp = config->timestamp;
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetTrackStartTimestamp(int id, uint32_t *timestamp) {
 int rtcGetTrackStartTimestamp(int id, uint32_t *timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		*timestamp = config->startTimestamp;
 		*timestamp = config->startTimestamp;
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp) {
 int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp) {
-	return WRAP({
+	return wrap([&] {
 		auto config = getRTPConfig(id);
 		auto config = getRTPConfig(id);
 		config->timestamp = timestamp;
 		config->timestamp = timestamp;
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
 int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
-	return WRAP({
-		auto sender = getRTCPSender(id);
+	return wrap([&] {
+		auto sender = getRtcpSrReporter(id);
 		*timestamp = sender->previousReportedTimestamp;
 		*timestamp = sender->previousReportedTimestamp;
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
-int rtcSetNeedsToSendRTCPSR(int id) {
-	return WRAP({
-		auto sender = getRTCPSender(id);
+int rtcSetNeedsToSendRtcpSr(int id) {
+	return wrap([id] {
+		auto sender = getRtcpSrReporter(id);
 		sender->setNeedsToReport();
 		sender->setNeedsToReport();
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA
 
 
 int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
 int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
-	return WRAP({
+	return wrap([&] {
 		if (!mediaDescriptionSdp)
 		if (!mediaDescriptionSdp)
 			throw std::invalid_argument("Unexpected null pointer for track media description");
 			throw std::invalid_argument("Unexpected null pointer for track media description");
 
 
@@ -652,7 +690,7 @@ int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
 }
 }
 
 
 int rtcDeleteTrack(int tr) {
 int rtcDeleteTrack(int tr) {
-	return WRAP({
+	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		track->onOpen(nullptr);
 		track->onOpen(nullptr);
 		track->onClosed(nullptr);
 		track->onClosed(nullptr);
@@ -662,11 +700,12 @@ int rtcDeleteTrack(int tr) {
 		track->onAvailable(nullptr);
 		track->onAvailable(nullptr);
 
 
 		eraseTrack(tr);
 		eraseTrack(tr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetTrackDescription(int tr, char *buffer, int size) {
 int rtcGetTrackDescription(int tr, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		return copyAndReturn(track->description(), buffer, size);
 		return copyAndReturn(track->description(), buffer, size);
 	});
 	});
@@ -674,7 +713,7 @@ int rtcGetTrackDescription(int tr, char *buffer, int size) {
 
 
 #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>();
 		ws->open(url);
 		ws->open(url);
 		return emplaceWebSocket(ws);
 		return emplaceWebSocket(ws);
@@ -682,7 +721,7 @@ int rtcCreateWebSocket(const char *url) {
 }
 }
 
 
 int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config) {
 int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config) {
-	return WRAP({
+	return wrap([&] {
 		WebSocket::Configuration c;
 		WebSocket::Configuration c;
 		c.disableTlsVerification = config->disableTlsVerification;
 		c.disableTlsVerification = config->disableTlsVerification;
 		auto ws = std::make_shared<WebSocket>(c);
 		auto ws = std::make_shared<WebSocket>(c);
@@ -692,7 +731,7 @@ int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config) {
 }
 }
 
 
 int rtcDeleteWebsocket(int ws) {
 int rtcDeleteWebsocket(int ws) {
-	return WRAP({
+	return wrap([&] {
 		auto webSocket = getWebSocket(ws);
 		auto webSocket = getWebSocket(ws);
 		webSocket->onOpen(nullptr);
 		webSocket->onOpen(nullptr);
 		webSocket->onClosed(nullptr);
 		webSocket->onClosed(nullptr);
@@ -702,12 +741,13 @@ int rtcDeleteWebsocket(int ws) {
 		webSocket->onAvailable(nullptr);
 		webSocket->onAvailable(nullptr);
 
 
 		eraseWebSocket(ws);
 		eraseWebSocket(ws);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 #endif
 #endif
 
 
 int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
 int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
 			peerConnection->onLocalDescription([pc, cb](Description desc) {
 			peerConnection->onLocalDescription([pc, cb](Description desc) {
@@ -716,11 +756,12 @@ int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			peerConnection->onLocalDescription(nullptr);
 			peerConnection->onLocalDescription(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
 int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
 			peerConnection->onLocalCandidate([pc, cb](Candidate cand) {
 			peerConnection->onLocalCandidate([pc, cb](Candidate cand) {
@@ -729,11 +770,12 @@ int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			peerConnection->onLocalCandidate(nullptr);
 			peerConnection->onLocalCandidate(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
 int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
 			peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
 			peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
@@ -742,11 +784,12 @@ int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			peerConnection->onStateChange(nullptr);
 			peerConnection->onStateChange(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
 int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
 			peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
 			peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
@@ -755,11 +798,12 @@ int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb)
 			});
 			});
 		else
 		else
 			peerConnection->onGatheringStateChange(nullptr);
 			peerConnection->onGatheringStateChange(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb) {
 int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
 			peerConnection->onSignalingStateChange([pc, cb](PeerConnection::SignalingState state) {
 			peerConnection->onSignalingStateChange([pc, cb](PeerConnection::SignalingState state) {
@@ -768,14 +812,15 @@ int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb)
 			});
 			});
 		else
 		else
 			peerConnection->onGatheringStateChange(nullptr);
 			peerConnection->onGatheringStateChange(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
 int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
-			peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
+			peerConnection->onDataChannel([pc, cb](shared_ptr<DataChannel> dataChannel) {
 				int dc = emplaceDataChannel(dataChannel);
 				int dc = emplaceDataChannel(dataChannel);
 				if (auto ptr = getUserPointer(pc)) {
 				if (auto ptr = getUserPointer(pc)) {
 					rtcSetUserPointer(dc, *ptr);
 					rtcSetUserPointer(dc, *ptr);
@@ -784,14 +829,15 @@ int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			peerConnection->onDataChannel(nullptr);
 			peerConnection->onDataChannel(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb) {
 int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		if (cb)
 		if (cb)
-			peerConnection->onTrack([pc, cb](std::shared_ptr<Track> track) {
+			peerConnection->onTrack([pc, cb](shared_ptr<Track> track) {
 				int tr = emplaceTrack(track);
 				int tr = emplaceTrack(track);
 				if (auto ptr = getUserPointer(pc)) {
 				if (auto ptr = getUserPointer(pc)) {
 					rtcSetUserPointer(tr, *ptr);
 					rtcSetUserPointer(tr, *ptr);
@@ -800,41 +846,45 @@ int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			peerConnection->onTrack(nullptr);
 			peerConnection->onTrack(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetLocalDescription(int pc, const char *type) {
 int rtcSetLocalDescription(int pc, const char *type) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 		peerConnection->setLocalDescription(type ? Description::stringToType(type)
 		peerConnection->setLocalDescription(type ? Description::stringToType(type)
 		                                         : Description::Type::Unspec);
 		                                         : Description::Type::Unspec);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
 int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (!sdp)
 		if (!sdp)
 			throw std::invalid_argument("Unexpected null pointer for remote description");
 			throw std::invalid_argument("Unexpected null pointer for remote description");
 
 
 		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
 		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
 int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (!cand)
 		if (!cand)
 			throw std::invalid_argument("Unexpected null pointer for remote candidate");
 			throw std::invalid_argument("Unexpected null pointer for remote candidate");
 
 
 		peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
 		peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetLocalDescription(int pc, char *buffer, int size) {
 int rtcGetLocalDescription(int pc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (auto desc = peerConnection->localDescription())
 		if (auto desc = peerConnection->localDescription())
@@ -845,7 +895,7 @@ int rtcGetLocalDescription(int pc, char *buffer, int size) {
 }
 }
 
 
 int rtcGetRemoteDescription(int pc, char *buffer, int size) {
 int rtcGetRemoteDescription(int pc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (auto desc = peerConnection->remoteDescription())
 		if (auto desc = peerConnection->remoteDescription())
@@ -855,8 +905,30 @@ int rtcGetRemoteDescription(int pc, char *buffer, int size) {
 	});
 	});
 }
 }
 
 
+int rtcGetLocalDescriptionType(int pc, char *buffer, int size) {
+	return wrap([&] {
+		auto peerConnection = getPeerConnection(pc);
+
+		if (auto desc = peerConnection->localDescription())
+			return copyAndReturn(desc->typeString(), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
+	});
+}
+
+int rtcGetRemoteDescriptionType(int pc, char *buffer, int size) {
+	return wrap([&] {
+		auto peerConnection = getPeerConnection(pc);
+
+		if (auto desc = peerConnection->remoteDescription())
+			return copyAndReturn(desc->typeString(), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
+	});
+}
+
 int rtcGetLocalAddress(int pc, char *buffer, int size) {
 int rtcGetLocalAddress(int pc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (auto addr = peerConnection->localAddress())
 		if (auto addr = peerConnection->localAddress())
@@ -867,7 +939,7 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
 }
 }
 
 
 int rtcGetRemoteAddress(int pc, char *buffer, int size) {
 int rtcGetRemoteAddress(int pc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (auto addr = peerConnection->remoteAddress())
 		if (auto addr = peerConnection->remoteAddress())
@@ -878,7 +950,7 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
 }
 }
 
 
 int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, int remoteSize) {
 int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, int remoteSize) {
-	return WRAP({
+	return wrap([&] {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		Candidate localCand;
 		Candidate localCand;
@@ -899,28 +971,28 @@ int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote
 }
 }
 
 
 int rtcGetDataChannelStream(int dc) {
 int rtcGetDataChannelStream(int dc) {
-	return WRAP({
+	return wrap([dc] {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 		return int(dataChannel->id());
 		return int(dataChannel->id());
 	});
 	});
 }
 }
 
 
 int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
 int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 		return copyAndReturn(dataChannel->label(), buffer, size);
 		return copyAndReturn(dataChannel->label(), buffer, size);
 	});
 	});
 }
 }
 
 
 int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
 int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 		return copyAndReturn(dataChannel->protocol(), buffer, size);
 		return copyAndReturn(dataChannel->protocol(), buffer, size);
 	});
 	});
 }
 }
 
 
 int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
-	return WRAP({
+	return wrap([&] {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 
 
 		if (!reliability)
 		if (!reliability)
@@ -931,10 +1003,10 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 		reliability->unordered = dcr.unordered;
 		reliability->unordered = dcr.unordered;
 		if (dcr.type == Reliability::Type::Timed) {
 		if (dcr.type == Reliability::Type::Timed) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxPacketLifeTime = unsigned(std::get<milliseconds>(dcr.rexmit).count());
+			reliability->maxPacketLifeTime = int(std::get<milliseconds>(dcr.rexmit).count());
 		} else if (dcr.type == Reliability::Type::Rexmit) {
 		} else if (dcr.type == Reliability::Type::Rexmit) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxRetransmits = unsigned(std::get<int>(dcr.rexmit));
+			reliability->maxRetransmits = std::get<int>(dcr.rexmit);
 		} else {
 		} else {
 			reliability->unreliable = false;
 			reliability->unreliable = false;
 		}
 		}
@@ -943,7 +1015,7 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 }
 }
 
 
 int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
 int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onOpen([id, cb]() {
 			channel->onOpen([id, cb]() {
@@ -952,11 +1024,12 @@ int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			channel->onOpen(nullptr);
 			channel->onOpen(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
 int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onClosed([id, cb]() {
 			channel->onClosed([id, cb]() {
@@ -965,11 +1038,12 @@ int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			channel->onClosed(nullptr);
 			channel->onClosed(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
 int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onError([id, cb](string error) {
 			channel->onError([id, cb](string error) {
@@ -978,11 +1052,12 @@ int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			channel->onError(nullptr);
 			channel->onError(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
 int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onMessage(
 			channel->onMessage(
@@ -996,11 +1071,12 @@ int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
 			    });
 			    });
 		else
 		else
 			channel->onMessage(nullptr);
 			channel->onMessage(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSendMessage(int id, const char *data, int size) {
 int rtcSendMessage(int id, const char *data, int size) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 
 
 		if (!data && size != 0)
 		if (!data && size != 0)
@@ -1020,21 +1096,22 @@ int rtcSendMessage(int id, const char *data, int size) {
 }
 }
 
 
 int rtcGetBufferedAmount(int id) {
 int rtcGetBufferedAmount(int id) {
-	return WRAP({
+	return wrap([id] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		return int(channel->bufferedAmount());
 		return int(channel->bufferedAmount());
 	});
 	});
 }
 }
 
 
 int rtcSetBufferedAmountLowThreshold(int id, int amount) {
 int rtcSetBufferedAmountLowThreshold(int id, int amount) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		channel->setBufferedAmountLowThreshold(size_t(amount));
 		channel->setBufferedAmountLowThreshold(size_t(amount));
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb) {
 int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onBufferedAmountLow([id, cb]() {
 			channel->onBufferedAmountLow([id, cb]() {
@@ -1043,15 +1120,16 @@ int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb)
 			});
 			});
 		else
 		else
 			channel->onBufferedAmountLow(nullptr);
 			channel->onBufferedAmountLow(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcGetAvailableAmount(int id) {
 int rtcGetAvailableAmount(int id) {
-	return WRAP({ return int(getChannel(id)->availableAmount()); });
+	return wrap([id] { return int(getChannel(id)->availableAmount()); });
 }
 }
 
 
 int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
 int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
 			channel->onAvailable([id, cb]() {
 			channel->onAvailable([id, cb]() {
@@ -1060,11 +1138,12 @@ int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
 			});
 			});
 		else
 		else
 			channel->onAvailable(nullptr);
 			channel->onAvailable(nullptr);
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcReceiveMessage(int id, char *buffer, int *size) {
 int rtcReceiveMessage(int id, char *buffer, int *size) {
-	return WRAP({
+	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 
 
 		if (!size)
 		if (!size)

+ 31 - 42
src/channel.cpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2019-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
@@ -17,76 +17,65 @@
  */
  */
 
 
 #include "channel.hpp"
 #include "channel.hpp"
+#include "globals.hpp"
+
+#include "impl/channel.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
+Channel::~Channel() {
+	impl()->resetCallbacks();
+}
 
 
-size_t Channel::bufferedAmount() const { return mBufferedAmount; }
+Channel::Channel(impl_ptr<impl::Channel> impl) : CheshireCat<impl::Channel>(std::move(impl)) {}
 
 
-size_t Channel::availableAmount() const { return 0; }
+size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
 
 
-void Channel::onOpen(std::function<void()> callback) { mOpenCallback = callback; }
+size_t Channel::bufferedAmount() const { return impl()->bufferedAmount; }
 
 
-void Channel::onClosed(std::function<void()> callback) { mClosedCallback = callback; }
+void Channel::onOpen(std::function<void()> callback) { impl()->openCallback = callback; }
 
 
-void Channel::onError(std::function<void(string error)> callback) { mErrorCallback = callback; }
+void Channel::onClosed(std::function<void()> callback) { impl()->closedCallback = callback; }
+
+void Channel::onError(std::function<void(string error)> callback) {
+	impl()->errorCallback = callback;
+}
 
 
 void Channel::onMessage(std::function<void(message_variant data)> callback) {
 void Channel::onMessage(std::function<void(message_variant data)> callback) {
-	mMessageCallback = callback;
+	impl()->messageCallback = callback;
 
 
 	// Pass pending messages
 	// Pass pending messages
 	while (auto message = receive())
 	while (auto message = receive())
-		mMessageCallback(*message);
+		impl()->messageCallback(*message);
 }
 }
 
 
 void Channel::onMessage(std::function<void(binary data)> binaryCallback,
 void Channel::onMessage(std::function<void(binary data)> binaryCallback,
                         std::function<void(string data)> stringCallback) {
                         std::function<void(string data)> stringCallback) {
-	onMessage([binaryCallback, stringCallback](std::variant<binary, string> data) {
+	onMessage([binaryCallback, stringCallback](variant<binary, string> data) {
 		std::visit(overloaded{binaryCallback, stringCallback}, std::move(data));
 		std::visit(overloaded{binaryCallback, stringCallback}, std::move(data));
 	});
 	});
 }
 }
 
 
 void Channel::onBufferedAmountLow(std::function<void()> callback) {
 void Channel::onBufferedAmountLow(std::function<void()> callback) {
-	mBufferedAmountLowCallback = callback;
+	impl()->bufferedAmountLowCallback = callback;
 }
 }
 
 
-void Channel::setBufferedAmountLowThreshold(size_t amount) { mBufferedAmountLowThreshold = amount; }
-
-void Channel::onAvailable(std::function<void()> callback) { mAvailableCallback = callback; }
-
-void Channel::triggerOpen() { mOpenCallback(); }
-
-void Channel::triggerClosed() { mClosedCallback(); }
-
-void Channel::triggerError(string error) { mErrorCallback(error); }
-
-void Channel::triggerAvailable(size_t count) {
-	if (count == 1)
-		mAvailableCallback();
+void Channel::setBufferedAmountLowThreshold(size_t amount) {
+	impl()->bufferedAmountLowThreshold = amount;
+}
 
 
-	while (mMessageCallback && count--) {
-		auto message = receive();
-		if (!message)
-			break;
-		mMessageCallback(*message);
-	}
+optional<message_variant> Channel::receive() {
+	return impl()->receive();
 }
 }
 
 
-void Channel::triggerBufferedAmount(size_t amount) {
-	size_t previous = mBufferedAmount.exchange(amount);
-	size_t threshold = mBufferedAmountLowThreshold.load();
-	if (previous > threshold && amount <= threshold)
-		mBufferedAmountLowCallback();
+optional<message_variant> Channel::peek() {
+	return impl()->peek();
 }
 }
 
 
-void Channel::resetCallbacks() {
-	mOpenCallback = nullptr;
-	mClosedCallback = nullptr;
-	mErrorCallback = nullptr;
-	mMessageCallback = nullptr;
-	mAvailableCallback = nullptr;
-	mBufferedAmountLowCallback = nullptr;
+size_t Channel::availableAmount() const {
+	return impl()->availableAmount();
 }
 }
 
 
+void Channel::onAvailable(std::function<void()> callback) { impl()->availableCallback = callback; }
+
 } // namespace rtc
 } // namespace rtc

+ 30 - 11
src/configuration.cpp

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

+ 22 - 294
src/datachannel.cpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2019-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
@@ -17,10 +17,12 @@
  */
  */
 
 
 #include "datachannel.hpp"
 #include "datachannel.hpp"
-#include "include.hpp"
-#include "logcounter.hpp"
+#include "globals.hpp"
+#include "common.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
-#include "sctptransport.hpp"
+
+#include "impl/datachannel.hpp"
+#include "impl/peerconnection.hpp"
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 #include <winsock2.h>
 #include <winsock2.h>
@@ -30,310 +32,36 @@
 
 
 namespace rtc {
 namespace rtc {
 
 
-LogCounter COUNTER_USERNEG_OPEN_MESSAGE(
-    plog::warning, "Number of open messages for a user-negotiated DataChannel received");
-
-using std::shared_ptr;
-using std::weak_ptr;
-using std::chrono::milliseconds;
-
-// Messages for the DataChannel establishment protocol
-// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09
-
-enum MessageType : uint8_t {
-	MESSAGE_OPEN_REQUEST = 0x00,
-	MESSAGE_OPEN_RESPONSE = 0x01,
-	MESSAGE_ACK = 0x02,
-	MESSAGE_OPEN = 0x03,
-	MESSAGE_CLOSE = 0x04
-};
-
-enum ChannelType : uint8_t {
-	CHANNEL_RELIABLE = 0x00,
-	CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01,
-	CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02
-};
-
-#pragma pack(push, 1)
-struct OpenMessage {
-	uint8_t type = MESSAGE_OPEN;
-	uint8_t channelType;
-	uint16_t priority;
-	uint32_t reliabilityParameter;
-	uint16_t labelLength;
-	uint16_t protocolLength;
-	// The following fields are:
-	// uint8_t[labelLength] label
-	// uint8_t[protocolLength] protocol
-};
-
-struct AckMessage {
-	uint8_t type = MESSAGE_ACK;
-};
-
-struct CloseMessage {
-	uint8_t type = MESSAGE_CLOSE;
-};
-#pragma pack(pop)
-
-DataChannel::DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
-                         string protocol, Reliability reliability)
-    : mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
-      mProtocol(std::move(protocol)),
-      mReliability(std::make_shared<Reliability>(std::move(reliability))),
-      mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
+DataChannel::DataChannel(impl_ptr<impl::DataChannel> impl)
+    : CheshireCat<impl::DataChannel>(impl),
+      Channel(std::dynamic_pointer_cast<impl::Channel>(impl)) {}
 
 
 DataChannel::~DataChannel() { close(); }
 DataChannel::~DataChannel() { close(); }
 
 
-uint16_t DataChannel::stream() const { return mStream; }
-
-uint16_t DataChannel::id() const { return uint16_t(mStream); }
-
-string DataChannel::label() const { return mLabel; }
-
-string DataChannel::protocol() const { return mProtocol; }
-
-Reliability DataChannel::reliability() const { return *mReliability; }
-
-void DataChannel::close() {
-	mIsClosed = true;
-	if (mIsOpen.exchange(false))
-		if (auto transport = mSctpTransport.lock())
-			transport->closeStream(mStream);
-
-	mSctpTransport.reset();
-	resetCallbacks();
-}
-
-void DataChannel::remoteClose() {
-	if (!mIsClosed.exchange(true))
-		triggerClosed();
-
-	mIsOpen = false;
-	mSctpTransport.reset();
-}
-
-bool DataChannel::send(message_variant data) { return outgoing(make_message(std::move(data))); }
-
-bool DataChannel::send(const byte *data, size_t size) {
-	return outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
-}
-
-std::optional<message_variant> DataChannel::receive() {
-	while (auto next = mRecvQueue.tryPop()) {
-		message_ptr message = *next;
-		if (message->type != Message::Control)
-			return to_variant(std::move(*message));
+void DataChannel::close() { return impl()->close(); }
 
 
-		auto raw = reinterpret_cast<const uint8_t *>(message->data());
-		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
-			remoteClose();
-	}
+uint16_t DataChannel::stream() const { return impl()->stream(); }
 
 
-	return nullopt;
-}
-
-std::optional<message_variant> DataChannel::peek() {
-	while (auto next = mRecvQueue.peek()) {
-		message_ptr message = *next;
-		if (message->type != Message::Control)
-			return to_variant(std::move(*message));
-
-		auto raw = reinterpret_cast<const uint8_t *>(message->data());
-		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
-			remoteClose();
-
-		mRecvQueue.tryPop();
-	}
+uint16_t DataChannel::id() const { return impl()->stream(); }
 
 
-	return nullopt;
-}
-
-bool DataChannel::isOpen(void) const { return mIsOpen; }
+string DataChannel::label() const { return impl()->label(); }
 
 
-bool DataChannel::isClosed(void) const { return mIsClosed; }
+string DataChannel::protocol() const { return impl()->protocol(); }
 
 
-size_t DataChannel::maxMessageSize() const {
-	size_t remoteMax = DEFAULT_MAX_MESSAGE_SIZE;
-	if (auto pc = mPeerConnection.lock())
-		if (auto description = pc->remoteDescription())
-			if (auto *application = description->application())
-				if (auto maxMessageSize = application->maxMessageSize())
-					remoteMax = *maxMessageSize > 0 ? *maxMessageSize : LOCAL_MAX_MESSAGE_SIZE;
+Reliability DataChannel::reliability() const { return impl()->reliability(); }
 
 
-	return std::min(remoteMax, LOCAL_MAX_MESSAGE_SIZE);
-}
+bool DataChannel::isOpen(void) const { return impl()->isOpen(); }
 
 
-size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
+bool DataChannel::isClosed(void) const { return impl()->isClosed(); }
 
 
-void DataChannel::open(shared_ptr<SctpTransport> transport) {
-	mSctpTransport = transport;
+size_t DataChannel::maxMessageSize() const { return impl()->maxMessageSize(); }
 
 
-	if (!mIsOpen.exchange(true))
-		triggerOpen();
+bool DataChannel::send(message_variant data) {
+	return impl()->outgoing(make_message(std::move(data)));
 }
 }
 
 
-void DataChannel::processOpenMessage(message_ptr) {
-	PLOG_DEBUG << "Received an open message for a user-negotiated DataChannel, ignoring";
-	COUNTER_USERNEG_OPEN_MESSAGE++;
-}
-
-bool DataChannel::outgoing(message_ptr message) {
-	if (mIsClosed)
-		throw std::runtime_error("DataChannel is closed");
-
-	if (message->size() > maxMessageSize())
-		throw std::runtime_error("Message size exceeds limit");
-
-	auto transport = mSctpTransport.lock();
-	if (!transport)
-		throw std::runtime_error("DataChannel transport is not open");
-
-	// Before the ACK has been received on a DataChannel, all messages must be sent ordered
-	message->reliability = mIsOpen ? mReliability : nullptr;
-	message->stream = mStream;
-	return transport->send(message);
-}
-
-void DataChannel::incoming(message_ptr message) {
-	if (!message)
-		return;
-
-	switch (message->type) {
-	case Message::Control: {
-		if (message->size() == 0)
-			break; // Ignore
-		auto raw = reinterpret_cast<const uint8_t *>(message->data());
-		switch (raw[0]) {
-		case MESSAGE_OPEN:
-			processOpenMessage(message);
-			break;
-		case MESSAGE_ACK:
-			if (!mIsOpen.exchange(true)) {
-				triggerOpen();
-			}
-			break;
-		case MESSAGE_CLOSE:
-			// The close message will be processed in-order in receive()
-			mRecvQueue.push(message);
-			triggerAvailable(mRecvQueue.size());
-			break;
-		default:
-			// Ignore
-			break;
-		}
-		break;
-	}
-	case Message::String:
-	case Message::Binary:
-		mRecvQueue.push(message);
-		triggerAvailable(mRecvQueue.size());
-		break;
-	default:
-		// Ignore
-		break;
-	}
-}
-
-NegociatedDataChannel::NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream,
-                                             string label, string protocol, Reliability reliability)
-    : DataChannel(pc, stream, std::move(label), std::move(protocol), std::move(reliability)) {}
-
-NegociatedDataChannel::NegociatedDataChannel(std::weak_ptr<PeerConnection> pc,
-                                             std::weak_ptr<SctpTransport> transport,
-                                             uint16_t stream)
-    : DataChannel(pc, stream, "", "", {}) {
-	mSctpTransport = transport;
-}
-
-NegociatedDataChannel::~NegociatedDataChannel() {}
-
-void NegociatedDataChannel::open(shared_ptr<SctpTransport> transport) {
-	mSctpTransport = transport;
-
-	uint8_t channelType;
-	uint32_t reliabilityParameter;
-	switch (mReliability->type) {
-	case Reliability::Type::Rexmit:
-		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
-		reliabilityParameter = uint32_t(std::get<int>(mReliability->rexmit));
-		break;
-
-	case Reliability::Type::Timed:
-		channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
-		reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
-		break;
-
-	default:
-		channelType = CHANNEL_RELIABLE;
-		reliabilityParameter = 0;
-		break;
-	}
-
-	if (mReliability->unordered)
-		channelType |= 0x80;
-
-	const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
-	binary buffer(len, byte(0));
-	auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
-	open.type = MESSAGE_OPEN;
-	open.channelType = channelType;
-	open.priority = htons(0);
-	open.reliabilityParameter = htonl(reliabilityParameter);
-	open.labelLength = htons(uint16_t(mLabel.size()));
-	open.protocolLength = htons(uint16_t(mProtocol.size()));
-
-	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
-	std::copy(mLabel.begin(), mLabel.end(), end);
-	std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
-
-	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
-}
-
-void NegociatedDataChannel::processOpenMessage(message_ptr message) {
-	auto transport = mSctpTransport.lock();
-	if (!transport)
-		throw std::runtime_error("DataChannel has no transport");
-
-	if (message->size() < sizeof(OpenMessage))
-		throw std::invalid_argument("DataChannel open message too small");
-
-	OpenMessage open = *reinterpret_cast<const OpenMessage *>(message->data());
-	open.priority = ntohs(open.priority);
-	open.reliabilityParameter = ntohl(open.reliabilityParameter);
-	open.labelLength = ntohs(open.labelLength);
-	open.protocolLength = ntohs(open.protocolLength);
-
-	if (message->size() < sizeof(OpenMessage) + size_t(open.labelLength + open.protocolLength))
-		throw std::invalid_argument("DataChannel open message truncated");
-
-	auto end = reinterpret_cast<const char *>(message->data() + sizeof(OpenMessage));
-	mLabel.assign(end, open.labelLength);
-	mProtocol.assign(end + open.labelLength, open.protocolLength);
-
-	mReliability->unordered = (open.channelType & 0x80) != 0;
-	switch (open.channelType & 0x7F) {
-	case CHANNEL_PARTIAL_RELIABLE_REXMIT:
-		mReliability->type = Reliability::Type::Rexmit;
-		mReliability->rexmit = int(open.reliabilityParameter);
-		break;
-	case CHANNEL_PARTIAL_RELIABLE_TIMED:
-		mReliability->type = Reliability::Type::Timed;
-		mReliability->rexmit = milliseconds(open.reliabilityParameter);
-		break;
-	default:
-		mReliability->type = Reliability::Type::Reliable;
-		mReliability->rexmit = int(0);
-	}
-
-	binary buffer(sizeof(AckMessage), byte(0));
-	auto &ack = *reinterpret_cast<AckMessage *>(buffer.data());
-	ack.type = MESSAGE_ACK;
-
-	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
-
-	if (!mIsOpen.exchange(true))
-		triggerOpen();
+bool DataChannel::send(const byte *data, size_t size) {
+	return impl()->outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc

+ 26 - 21
src/description.cpp

@@ -28,7 +28,6 @@
 #include <sstream>
 #include <sstream>
 #include <unordered_map>
 #include <unordered_map>
 
 
-using std::shared_ptr;
 using std::chrono::system_clock;
 using std::chrono::system_clock;
 
 
 namespace {
 namespace {
@@ -59,7 +58,13 @@ inline std::pair<string_view, string_view> parse_pair(string_view attr) {
 }
 }
 
 
 template <typename T> T to_integer(string_view s) {
 template <typename T> T to_integer(string_view s) {
-	return std::is_signed<T>::value ? T(std::stol(string(s))) : T(std::stoul(string(s)));
+	const string str(s);
+	try {
+		return std::is_signed<T>::value ? T(std::stol(str)) : T(std::stoul(str));
+	}
+	catch(...) {
+		throw std::invalid_argument("Invalid integer \"" + str + "\" in description");
+	}
 }
 }
 
 
 } // namespace
 } // namespace
@@ -71,7 +76,7 @@ Description::Description(const string &sdp, Type type, Role role)
 	hintType(type);
 	hintType(type);
 
 
 	int index = -1;
 	int index = -1;
-	std::shared_ptr<Entry> current;
+	shared_ptr<Entry> current;
 	std::istringstream ss(sdp);
 	std::istringstream ss(sdp);
 	while (ss) {
 	while (ss) {
 		string line;
 		string line;
@@ -151,11 +156,11 @@ string Description::bundleMid() const {
 	return !mEntries.empty() ? mEntries[0]->mid() : "0";
 	return !mEntries.empty() ? mEntries[0]->mid() : "0";
 }
 }
 
 
-std::optional<string> Description::iceUfrag() const { return mIceUfrag; }
+optional<string> Description::iceUfrag() const { return mIceUfrag; }
 
 
-std::optional<string> Description::icePwd() const { return mIcePwd; }
+optional<string> Description::icePwd() const { return mIcePwd; }
 
 
-std::optional<string> Description::fingerprint() const { return mFingerprint; }
+optional<string> Description::fingerprint() const { return mFingerprint; }
 
 
 bool Description::ended() const { return mEnded; }
 bool Description::ended() const { return mEnded; }
 
 
@@ -314,9 +319,9 @@ string Description::generateApplicationSdp(string_view eol) const {
 	return sdp.str();
 	return sdp.str();
 }
 }
 
 
-std::optional<Candidate> Description::defaultCandidate() const {
+optional<Candidate> Description::defaultCandidate() const {
 	// Return the first host candidate with highest priority, favoring IPv4
 	// Return the first host candidate with highest priority, favoring IPv4
-	std::optional<Candidate> result;
+	optional<Candidate> result;
 	for (const auto &c : mCandidates) {
 	for (const auto &c : mCandidates) {
 		if (c.type() == Candidate::Type::Host) {
 		if (c.type() == Candidate::Type::Host) {
 			if (!result ||
 			if (!result ||
@@ -396,7 +401,7 @@ int Description::addAudio(string mid, Direction dir) {
 	return addMedia(Audio(std::move(mid), dir));
 	return addMedia(Audio(std::move(mid), dir));
 }
 }
 
 
-std::variant<Description::Media *, Description::Application *>
+variant<Description::Media *, Description::Application *>
 Description::media(unsigned int index) {
 Description::media(unsigned int index) {
 	if (index >= mEntries.size())
 	if (index >= mEntries.size())
 		throw std::out_of_range("Media index out of range");
 		throw std::out_of_range("Media index out of range");
@@ -415,7 +420,7 @@ Description::media(unsigned int index) {
 	}
 	}
 }
 }
 
 
-std::variant<const Description::Media *, const Description::Application *>
+variant<const Description::Media *, const Description::Application *>
 Description::media(unsigned int index) const {
 Description::media(unsigned int index) const {
 	if (index >= mEntries.size())
 	if (index >= mEntries.size())
 		throw std::out_of_range("Media index out of range");
 		throw std::out_of_range("Media index out of range");
@@ -518,21 +523,21 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
 	return mAttributes.erase(it);
 	return mAttributes.erase(it);
 }
 }
 
 
-void Description::Media::addSSRC(uint32_t ssrc, std::optional<string> name,
-                                 std::optional<string> msid) {
+void Description::Media::addSSRC(uint32_t ssrc, optional<string> name,
+								 optional<string> msid, optional<string> trackID) {
 	if (name)
 	if (name)
 		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
 		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
 	else
 	else
 		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
 		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
 
 
 	if (msid)
 	if (msid)
-		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + *msid);
+		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + trackID.value_or(*msid));
 
 
 	mSsrcs.emplace_back(ssrc);
 	mSsrcs.emplace_back(ssrc);
 }
 }
 
 
-void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
-                                     std::optional<string> msid) {
+void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
+									 optional<string> msid, optional<string> trackID) {
 	auto it = mAttributes.begin();
 	auto it = mAttributes.begin();
 	while (it != mAttributes.end()) {
 	while (it != mAttributes.end()) {
 		if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
 		if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
@@ -540,7 +545,7 @@ void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optio
 		} else
 		} else
 			it++;
 			it++;
 	}
 	}
-	addSSRC(ssrc, std::move(name), std::move(msid));
+	addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
 }
 }
 
 
 void Description::Media::removeSSRC(uint32_t oldSSRC) {
 void Description::Media::removeSSRC(uint32_t oldSSRC) {
@@ -704,7 +709,7 @@ void Description::Media::removeFormat(const string &fmt) {
 }
 }
 
 
 void Description::Video::addVideoCodec(int payloadType, string codec,
 void Description::Video::addVideoCodec(int payloadType, string codec,
-                                       std::optional<string> profile) {
+                                       optional<string> profile) {
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
 	map.addFB("nack");
 	map.addFB("nack");
 	map.addFB("nack pli");
 	map.addFB("nack pli");
@@ -729,7 +734,7 @@ void Description::Video::addVideoCodec(int payloadType, string codec,
 }
 }
 
 
 void Description::Audio::addAudioCodec(int payloadType, string codec,
 void Description::Audio::addAudioCodec(int payloadType, string codec,
-                                       std::optional<string> profile) {
+                                       optional<string> profile) {
 	// TODO This 48000/2 should be parameterized
 	// TODO This 48000/2 should be parameterized
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
 	if (profile)
 	if (profile)
@@ -744,7 +749,7 @@ void Description::Media::addRTXCodec(unsigned int payloadType, unsigned int orig
 	addRTPMap(map);
 	addRTPMap(map);
 }
 }
 
 
-void Description::Video::addH264Codec(int pt, std::optional<string> profile) {
+void Description::Video::addH264Codec(int pt, optional<string> profile) {
 	addVideoCodec(pt, "H264", profile);
 	addVideoCodec(pt, "H264", profile);
 }
 }
 
 
@@ -823,7 +828,7 @@ void Description::Media::parseSdpLine(string_view line) {
 		} else if (key == "rtcp-mux") {
 		} else if (key == "rtcp-mux") {
 			// always added
 			// always added
 		} else if (key == "ssrc") {
 		} else if (key == "ssrc") {
-			mSsrcs.emplace_back(std::stoul(string(value)));
+			mSsrcs.emplace_back(to_integer<uint32_t>(value));
 		} else {
 		} else {
 			Entry::parseSdpLine(line);
 			Entry::parseSdpLine(line);
 		}
 		}
@@ -900,7 +905,7 @@ void Description::Media::RTPMap::setMLine(string_view mline) {
 Description::Audio::Audio(string mid, Direction dir)
 Description::Audio::Audio(string mid, Direction dir)
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
 
-void Description::Audio::addOpusCodec(int payloadType, std::optional<string> profile) {
+void Description::Audio::addOpusCodec(int payloadType, optional<string> profile) {
 	addAudioCodec(payloadType, "OPUS", profile);
 	addAudioCodec(payloadType, "OPUS", profile);
 }
 }
 
 

+ 41 - 0
src/globals.hpp

@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2019-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
+ */
+
+#ifndef RTC_GLOBALS_H
+#define RTC_GLOBALS_H
+
+#include "common.hpp"
+
+namespace rtc {
+
+const size_t MAX_NUMERICNODE_LEN = 48; // Max IPv6 string representation length
+const size_t MAX_NUMERICSERV_LEN = 6;  // Max port string representation length
+
+const uint16_t DEFAULT_SCTP_PORT = 5000;          // SCTP port to use by default
+const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536;    // Remote max message size if not specified in SDP
+const size_t LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Local max message size
+
+const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
+
+const int THREADPOOL_SIZE = 4; // Number of threads in the global thread pool (>= 2)
+
+const size_t DEFAULT_MTU = RTC_DEFAULT_MTU; // defined in rtc.h
+
+} // namespace rtc
+
+#endif

+ 1 - 140
src/h264packetizationhandler.cpp

@@ -22,146 +22,7 @@
 
 
 namespace rtc {
 namespace rtc {
 
 
-using std::function;
-using std::make_shared;
-using std::shared_ptr;
-
-typedef enum {
-	NUSM_noMatch,
-	NUSM_firstZero,
-	NUSM_secondZero,
-	NUSM_thirdZero,
-	NUSM_shortMatch,
-	NUSM_longMatch
-} NalUnitStartSequenceMatch;
-
-NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match, byte _byte,
-                                                 H264PacketizationHandler::Separator separator) {
-	assert(separator != H264PacketizationHandler::Separator::Length);
-	auto byte = (uint8_t)_byte;
-	auto detectShort = separator == H264PacketizationHandler::Separator::ShortStartSequence ||
-	                   separator == H264PacketizationHandler::Separator::StartSequence;
-	auto detectLong = separator == H264PacketizationHandler::Separator::LongStartSequence ||
-	                  separator == H264PacketizationHandler::Separator::StartSequence;
-	switch (match) {
-	case NUSM_noMatch:
-		if (byte == 0x00) {
-			return NUSM_firstZero;
-		}
-		break;
-	case NUSM_firstZero:
-		if (byte == 0x00) {
-			return NUSM_secondZero;
-		}
-		break;
-	case NUSM_secondZero:
-		if (byte == 0x00 && detectLong) {
-			return NUSM_thirdZero;
-		} else if (byte == 0x01 && detectShort) {
-			return NUSM_shortMatch;
-		}
-		break;
-	case NUSM_thirdZero:
-		if (byte == 0x01 && detectLong) {
-			return NUSM_longMatch;
-		}
-		break;
-	case NUSM_shortMatch:
-		return NUSM_shortMatch;
-	case NUSM_longMatch:
-		return NUSM_longMatch;
-	}
-	return NUSM_noMatch;
-}
-
-message_ptr H264PacketizationHandler::incoming(message_ptr ptr) { return ptr; }
-
-shared_ptr<NalUnits> H264PacketizationHandler::splitMessage(message_ptr message) {
-	auto nalus = make_shared<NalUnits>();
-	if (separator == Separator::Length) {
-		unsigned long long index = 0;
-		while (index < message->size()) {
-			assert(index + 4 < message->size());
-			if (index + 4 >= message->size()) {
-				LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
-				break;
-			}
-			auto lengthPtr = (uint32_t *)(message->data() + index);
-			uint32_t length = ntohl(*lengthPtr);
-			auto naluStartIndex = index + 4;
-			auto naluEndIndex = naluStartIndex + length;
-
-			assert(naluEndIndex <= message->size());
-			if (naluEndIndex > message->size()) {
-				LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
-				break;
-			}
-			nalus->push_back(
-			    NalUnit(message->begin() + naluStartIndex, message->begin() + naluEndIndex));
-			index = naluEndIndex;
-		}
-	} else {
-		NalUnitStartSequenceMatch match = NUSM_noMatch;
-		unsigned long long index = 0;
-		while (index < message->size()) {
-			match = StartSequenceMatchSucc(match, (*message)[index++], separator);
-			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
-				match = NUSM_noMatch;
-				break;
-			}
-		}
-		index++;
-		unsigned long long naluStartIndex = index;
-
-		while (index < message->size()) {
-			match = StartSequenceMatchSucc(match, (*message)[index], separator);
-			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
-				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
-				unsigned long long naluEndIndex = index - sequenceLength;
-				match = NUSM_noMatch;
-				nalus->push_back(NalUnit(message->begin() + naluStartIndex,
-				                         message->begin() + naluEndIndex + 1));
-				naluStartIndex = index + 1;
-			}
-			index++;
-		}
-		nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->end()));
-	}
-	return nalus;
-}
-
-message_ptr H264PacketizationHandler::outgoing(message_ptr ptr) {
-	if (ptr->type == Message::Binary) {
-		auto nalus = splitMessage(ptr);
-		auto fragments = nalus->generateFragments(maximumFragmentSize);
-
-		auto lastPacket = withStatsRecording<message_ptr>(
-		    [fragments, this](function<void(message_ptr)> addToReport) {
-			    for (unsigned long long index = 0; index < fragments.size() - 1; index++) {
-				    auto packet = packetizer->packetize(fragments[index], false);
-
-				    addToReport(packet);
-
-				    outgoingCallback(std::move(packet));
-			    }
-			    // packet is last, marker must be set
-			    auto lastPacket = packetizer->packetize(fragments[fragments.size() - 1], true);
-			    addToReport(lastPacket);
-			    return lastPacket;
-		    });
-		return lastPacket;
-	}
-	return ptr;
-}
-
-H264PacketizationHandler::H264PacketizationHandler(Separator separator,
-                                                   std::shared_ptr<H264RtpPacketizer> packetizer,
-                                                   uint16_t maximumFragmentSize)
-    : RtcpHandler(), RtcpSenderReporter(packetizer->rtpConfig), packetizer(packetizer),
-      maximumFragmentSize(maximumFragmentSize), separator(separator) {
-
-	senderReportOutgoingCallback = [this](message_ptr msg) { outgoingCallback(msg); };
-}
+H264PacketizationHandler::H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer): MediaChainableHandler(packetizer) { }
 
 
 } // namespace rtc
 } // namespace rtc
 
 

+ 130 - 2
src/h264rtppacketizer.cpp

@@ -22,8 +22,136 @@
 
 
 namespace rtc {
 namespace rtc {
 
 
-H264RtpPacketizer::H264RtpPacketizer(std::shared_ptr<RtpPacketizationConfig> rtpConfig)
-    : RtpPacketizer(rtpConfig) {}
+typedef enum {
+	NUSM_noMatch,
+	NUSM_firstZero,
+	NUSM_secondZero,
+	NUSM_thirdZero,
+	NUSM_shortMatch,
+	NUSM_longMatch
+} NalUnitStartSequenceMatch;
+
+NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match, byte _byte,
+												 H264RtpPacketizer::Separator separator) {
+	assert(separator != H264RtpPacketizer::Separator::Length);
+	auto byte = (uint8_t)_byte;
+	auto detectShort = separator == H264RtpPacketizer::Separator::ShortStartSequence ||
+	separator == H264RtpPacketizer::Separator::StartSequence;
+	auto detectLong = separator == H264RtpPacketizer::Separator::LongStartSequence ||
+	separator == H264RtpPacketizer::Separator::StartSequence;
+	switch (match) {
+		case NUSM_noMatch:
+			if (byte == 0x00) {
+				return NUSM_firstZero;
+			}
+			break;
+		case NUSM_firstZero:
+			if (byte == 0x00) {
+				return NUSM_secondZero;
+			}
+			break;
+		case NUSM_secondZero:
+			if (byte == 0x00 && detectLong) {
+				return NUSM_thirdZero;
+			} else if (byte == 0x01 && detectShort) {
+				return NUSM_shortMatch;
+			}
+			break;
+		case NUSM_thirdZero:
+			if (byte == 0x01 && detectLong) {
+				return NUSM_longMatch;
+			}
+			break;
+		case NUSM_shortMatch:
+			return NUSM_shortMatch;
+		case NUSM_longMatch:
+			return NUSM_longMatch;
+	}
+	return NUSM_noMatch;
+}
+
+shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
+	auto nalus = std::make_shared<NalUnits>();
+	if (separator == Separator::Length) {
+		unsigned long long index = 0;
+		while (index < message->size()) {
+			assert(index + 4 < message->size());
+			if (index + 4 >= message->size()) {
+				LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
+				break;
+			}
+			auto lengthPtr = (uint32_t *)(message->data() + index);
+			uint32_t length = ntohl(*lengthPtr);
+			auto naluStartIndex = index + 4;
+			auto naluEndIndex = naluStartIndex + length;
+
+			assert(naluEndIndex <= message->size());
+			if (naluEndIndex > message->size()) {
+				LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
+				break;
+			}
+			auto begin = message->begin() + naluStartIndex;
+			auto end = message->begin() + naluEndIndex;
+			nalus->push_back(std::make_shared<NalUnit>(begin, end));
+			index = naluEndIndex;
+		}
+	} else {
+		NalUnitStartSequenceMatch match = NUSM_noMatch;
+		unsigned long long index = 0;
+		while (index < message->size()) {
+			match = StartSequenceMatchSucc(match, (*message)[index++], separator);
+			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+				match = NUSM_noMatch;
+				break;
+			}
+		}
+
+		unsigned long long naluStartIndex = index;
+
+		while (index < message->size()) {
+			match = StartSequenceMatchSucc(match, (*message)[index], separator);
+			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
+				unsigned long long naluEndIndex = index - sequenceLength;
+				match = NUSM_noMatch;
+				auto begin = message->begin() + naluStartIndex;
+				auto end = message->begin() + naluEndIndex + 1;
+				nalus->push_back(std::make_shared<NalUnit>(begin, end));
+				naluStartIndex = index + 1;
+			}
+			index++;
+		}
+		auto begin = message->begin() + naluStartIndex;
+		auto end = message->end();
+		nalus->push_back(std::make_shared<NalUnit>(begin, end));
+	}
+	return nalus;
+}
+
+H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+									 uint16_t maximumFragmentSize)
+: RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize), separator(Separator::Length) {}
+
+H264RtpPacketizer::H264RtpPacketizer(H264RtpPacketizer::Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
+									 uint16_t maximumFragmentSize)
+: RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize), separator(separator) {}
+
+ChainedOutgoingProduct H264RtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages, message_ptr control) {
+	ChainedMessagesProduct packets = std::make_shared<std::vector<binary_ptr>>();
+	for (auto message: *messages) {
+		auto nalus = splitMessage(message);
+		auto fragments = nalus->generateFragments(maximumFragmentSize);
+		if (fragments.size() == 0) {
+			return ChainedOutgoingProduct();
+		}
+		unsigned i = 0;
+		for (; i < fragments.size() - 1; i++) {
+			packets->push_back(packetize(fragments[i], false));
+		}
+		packets->push_back(packetize(fragments[i], true));
+	}
+	return {packets, control};
+}
 
 
 } // namespace rtc
 } // namespace rtc
 
 

+ 2 - 2
src/base64.cpp → src/impl/base64.cpp

@@ -20,7 +20,7 @@
 
 
 #include "base64.hpp"
 #include "base64.hpp"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 using std::to_integer;
 using std::to_integer;
 
 
@@ -59,6 +59,6 @@ string to_base64(const binary &data) {
 	return out;
 	return out;
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 4 - 4
src/base64.hpp → src/impl/base64.hpp

@@ -16,14 +16,14 @@
  * 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_BASE64_H
-#define RTC_BASE64_H
+#ifndef RTC_IMPL_BASE64_H
+#define RTC_IMPL_BASE64_H
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 string to_base64(const binary &data);
 string to_base64(const binary &data);
 
 

+ 3 - 15
src/certificate.cpp → src/impl/certificate.cpp

@@ -26,14 +26,10 @@
 #include <sstream>
 #include <sstream>
 #include <unordered_map>
 #include <unordered_map>
 
 
-using std::shared_ptr;
-using std::string;
-using std::unique_ptr;
+namespace rtc::impl {
 
 
 #if USE_GNUTLS
 #if USE_GNUTLS
 
 
-namespace rtc {
-
 Certificate::Certificate(string crt_pem, string key_pem)
 Certificate::Certificate(string crt_pem, string key_pem)
     : mCredentials(gnutls::new_credentials(), gnutls::free_credentials) {
     : mCredentials(gnutls::new_credentials(), gnutls::free_credentials) {
 
 
@@ -57,7 +53,7 @@ Certificate::Certificate(string crt_pem, string key_pem)
 		gnutls_free(crt_list);
 		gnutls_free(crt_list);
 	};
 	};
 
 
-	std::unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(),
+	unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(),
 	                                                                     free_crt_list);
 	                                                                     free_crt_list);
 
 
 	mFingerprint = make_fingerprint(*crt_list);
 	mFingerprint = make_fingerprint(*crt_list);
@@ -129,12 +125,8 @@ certificate_ptr make_certificate_impl(string commonName) {
 
 
 } // namespace
 } // namespace
 
 
-} // namespace rtc
-
 #else // USE_GNUTLS==0
 #else // USE_GNUTLS==0
 
 
-namespace rtc {
-
 Certificate::Certificate(string crt_pem, string key_pem) {
 Certificate::Certificate(string crt_pem, string key_pem) {
 	BIO *bio = BIO_new(BIO_s_mem());
 	BIO *bio = BIO_new(BIO_s_mem());
 	BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
 	BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
@@ -230,14 +222,10 @@ certificate_ptr make_certificate_impl(string commonName) {
 
 
 } // namespace
 } // namespace
 
 
-} // namespace rtc
-
 #endif
 #endif
 
 
 // Common for GnuTLS and OpenSSL
 // Common for GnuTLS and OpenSSL
 
 
-namespace rtc {
-
 namespace {
 namespace {
 
 
 static std::unordered_map<string, future_certificate_ptr> CertificateCache;
 static std::unordered_map<string, future_certificate_ptr> CertificateCache;
@@ -262,4 +250,4 @@ void CleanupCertificateCache() {
 	CertificateCache.clear();
 	CertificateCache.clear();
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl

+ 10 - 10
src/certificate.hpp → src/impl/certificate.hpp

@@ -16,16 +16,16 @@
  * 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_CERTIFICATE_H
-#define RTC_CERTIFICATE_H
+#ifndef RTC_IMPL_CERTIFICATE_H
+#define RTC_IMPL_CERTIFICATE_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "tls.hpp"
 #include "tls.hpp"
 
 
 #include <future>
 #include <future>
 #include <tuple>
 #include <tuple>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class Certificate {
 class Certificate {
 public:
 public:
@@ -35,7 +35,7 @@ public:
 	Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
 	Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
 	gnutls_certificate_credentials_t credentials() const;
 	gnutls_certificate_credentials_t credentials() const;
 #else
 #else
-	Certificate(std::shared_ptr<X509> x509, std::shared_ptr<EVP_PKEY> pkey);
+	Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey);
 	std::tuple<X509 *, EVP_PKEY *> credentials() const;
 	std::tuple<X509 *, EVP_PKEY *> credentials() const;
 #endif
 #endif
 
 
@@ -43,10 +43,10 @@ public:
 
 
 private:
 private:
 #if USE_GNUTLS
 #if USE_GNUTLS
-	std::shared_ptr<gnutls_certificate_credentials_t> mCredentials;
+	shared_ptr<gnutls_certificate_credentials_t> mCredentials;
 #else
 #else
-	std::shared_ptr<X509> mX509;
-	std::shared_ptr<EVP_PKEY> mPKey;
+	shared_ptr<X509> mX509;
+	shared_ptr<EVP_PKEY> mPKey;
 #endif
 #endif
 
 
 	string mFingerprint;
 	string mFingerprint;
@@ -58,13 +58,13 @@ string make_fingerprint(gnutls_x509_crt_t crt);
 string make_fingerprint(X509 *x509);
 string make_fingerprint(X509 *x509);
 #endif
 #endif
 
 
-using certificate_ptr = std::shared_ptr<Certificate>;
+using certificate_ptr = shared_ptr<Certificate>;
 using future_certificate_ptr = std::shared_future<certificate_ptr>;
 using future_certificate_ptr = std::shared_future<certificate_ptr>;
 
 
 future_certificate_ptr make_certificate(string commonName = "libdatachannel"); // cached
 future_certificate_ptr make_certificate(string commonName = "libdatachannel"); // cached
 
 
 void CleanupCertificateCache();
 void CleanupCertificateCache();
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 57 - 0
src/impl/channel.cpp

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2019-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 "channel.hpp"
+
+namespace rtc::impl {
+
+void Channel::triggerOpen() { openCallback(); }
+
+void Channel::triggerClosed() { closedCallback(); }
+
+void Channel::triggerError(string error) { errorCallback(error); }
+
+void Channel::triggerAvailable(size_t count) {
+	if (count == 1)
+		availableCallback();
+
+	while (messageCallback && count--) {
+		auto message = receive();
+		if (!message)
+			break;
+		messageCallback(*message);
+	}
+}
+
+void Channel::triggerBufferedAmount(size_t amount) {
+	size_t previous = bufferedAmount.exchange(amount);
+	size_t threshold = bufferedAmountLowThreshold.load();
+	if (previous > threshold && amount <= threshold)
+		bufferedAmountLowCallback();
+}
+
+void Channel::resetCallbacks() {
+	openCallback = nullptr;
+	closedCallback = nullptr;
+	errorCallback = nullptr;
+	messageCallback = nullptr;
+	availableCallback = nullptr;
+	bufferedAmountLowCallback = nullptr;
+}
+
+} // namespace rtc::impl

+ 56 - 0
src/impl/channel.hpp

@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2019-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
+ */
+
+#ifndef RTC_IMPL_CHANNEL_H
+#define RTC_IMPL_CHANNEL_H
+
+#include "common.hpp"
+#include "message.hpp"
+
+#include <atomic>
+#include <functional>
+
+namespace rtc::impl {
+
+struct Channel {
+	virtual optional<message_variant> receive() = 0;
+	virtual optional<message_variant> peek() = 0;
+	virtual size_t availableAmount() const = 0;
+
+	virtual void triggerOpen();
+	virtual void triggerClosed();
+	virtual void triggerError(string error);
+	virtual void triggerAvailable(size_t count);
+	virtual void triggerBufferedAmount(size_t amount);
+
+	virtual void resetCallbacks();
+
+	synchronized_callback<> openCallback;
+	synchronized_callback<> closedCallback;
+	synchronized_callback<string> errorCallback;
+	synchronized_callback<message_variant> messageCallback;
+	synchronized_callback<> availableCallback;
+	synchronized_callback<> bufferedAmountLowCallback;
+
+	std::atomic<size_t> bufferedAmount = 0;
+	std::atomic<size_t> bufferedAmountLowThreshold = 0;
+};
+
+} // namespace rtc::impl
+
+#endif

+ 365 - 0
src/impl/datachannel.cpp

@@ -0,0 +1,365 @@
+/**
+ * Copyright (c) 2019-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 "datachannel.hpp"
+#include "common.hpp"
+#include "globals.hpp"
+#include "logcounter.hpp"
+#include "peerconnection.hpp"
+#include "sctptransport.hpp"
+
+#include "rtc/datachannel.hpp"
+#include "rtc/track.hpp"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+using std::chrono::milliseconds;
+
+namespace rtc::impl {
+
+// Messages for the DataChannel establishment protocol
+// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09
+
+enum MessageType : uint8_t {
+	MESSAGE_OPEN_REQUEST = 0x00,
+	MESSAGE_OPEN_RESPONSE = 0x01,
+	MESSAGE_ACK = 0x02,
+	MESSAGE_OPEN = 0x03,
+	MESSAGE_CLOSE = 0x04
+};
+
+enum ChannelType : uint8_t {
+	CHANNEL_RELIABLE = 0x00,
+	CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01,
+	CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02
+};
+
+#pragma pack(push, 1)
+struct OpenMessage {
+	uint8_t type = MESSAGE_OPEN;
+	uint8_t channelType;
+	uint16_t priority;
+	uint32_t reliabilityParameter;
+	uint16_t labelLength;
+	uint16_t protocolLength;
+	// The following fields are:
+	// uint8_t[labelLength] label
+	// uint8_t[protocolLength] protocol
+};
+
+struct AckMessage {
+	uint8_t type = MESSAGE_ACK;
+};
+
+struct CloseMessage {
+	uint8_t type = MESSAGE_CLOSE;
+};
+#pragma pack(pop)
+
+LogCounter COUNTER_USERNEG_OPEN_MESSAGE(
+    plog::warning, "Number of open messages for a user-negotiated DataChannel received");
+
+DataChannel::DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
+                         string protocol, Reliability reliability)
+    : mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
+      mProtocol(std::move(protocol)),
+      mReliability(std::make_shared<Reliability>(std::move(reliability))),
+      mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
+
+DataChannel::~DataChannel() { close(); }
+
+void DataChannel::close() {
+	shared_ptr<SctpTransport> transport;
+	{
+		std::shared_lock lock(mMutex);
+		transport = mSctpTransport.lock();
+	}
+
+	mIsClosed = true;
+	if (mIsOpen.exchange(false) && transport)
+		transport->closeStream(mStream);
+
+	resetCallbacks();
+}
+
+void DataChannel::remoteClose() {
+	if (!mIsClosed.exchange(true))
+		triggerClosed();
+
+	mIsOpen = false;
+}
+
+optional<message_variant> DataChannel::receive() {
+	while (auto next = mRecvQueue.tryPop()) {
+		message_ptr message = *next;
+		if (message->type != Message::Control)
+			return to_variant(std::move(*message));
+
+		auto raw = reinterpret_cast<const uint8_t *>(message->data());
+		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
+			remoteClose();
+	}
+
+	return nullopt;
+}
+
+optional<message_variant> DataChannel::peek() {
+	while (auto next = mRecvQueue.peek()) {
+		message_ptr message = *next;
+		if (message->type != Message::Control)
+			return to_variant(std::move(*message));
+
+		auto raw = reinterpret_cast<const uint8_t *>(message->data());
+		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
+			remoteClose();
+
+		mRecvQueue.tryPop();
+	}
+
+	return nullopt;
+}
+
+size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
+
+uint16_t DataChannel::stream() const {
+	std::shared_lock lock(mMutex);
+	return mStream;
+}
+
+string DataChannel::label() const {
+	std::shared_lock lock(mMutex);
+	return mLabel;
+}
+
+string DataChannel::protocol() const {
+	std::shared_lock lock(mMutex);
+	return mProtocol;
+}
+
+Reliability DataChannel::reliability() const {
+	std::shared_lock lock(mMutex);
+	return *mReliability;
+}
+
+bool DataChannel::isOpen(void) const { return mIsOpen; }
+
+bool DataChannel::isClosed(void) const { return mIsClosed; }
+
+size_t DataChannel::maxMessageSize() const {
+	size_t remoteMax = DEFAULT_MAX_MESSAGE_SIZE;
+	if (auto pc = mPeerConnection.lock())
+		if (auto description = pc->remoteDescription())
+			if (auto *application = description->application())
+				if (auto maxMessageSize = application->maxMessageSize())
+					remoteMax = *maxMessageSize > 0 ? *maxMessageSize : LOCAL_MAX_MESSAGE_SIZE;
+
+	return std::min(remoteMax, LOCAL_MAX_MESSAGE_SIZE);
+}
+
+void DataChannel::shiftStream() {
+	if (mStream % 2 == 1)
+		mStream -= 1;
+}
+
+void DataChannel::open(shared_ptr<SctpTransport> transport) {
+	{
+		std::unique_lock lock(mMutex);
+		mSctpTransport = transport;
+	}
+
+	if (!mIsOpen.exchange(true))
+		triggerOpen();
+}
+
+void DataChannel::processOpenMessage(message_ptr) {
+	PLOG_DEBUG << "Received an open message for a user-negotiated DataChannel, ignoring";
+	COUNTER_USERNEG_OPEN_MESSAGE++;
+}
+
+bool DataChannel::outgoing(message_ptr message) {
+	shared_ptr<SctpTransport> transport;
+	{
+		std::shared_lock lock(mMutex);
+		transport = mSctpTransport.lock();
+
+		if (!transport || mIsClosed)
+			throw std::runtime_error("DataChannel is closed");
+
+		if (message->size() > maxMessageSize())
+			throw std::runtime_error("Message size exceeds limit");
+
+		// Before the ACK has been received on a DataChannel, all messages must be sent ordered
+		message->reliability = mIsOpen ? mReliability : nullptr;
+		message->stream = mStream;
+	}
+
+	return transport->send(message);
+}
+
+void DataChannel::incoming(message_ptr message) {
+	if (!message)
+		return;
+
+	switch (message->type) {
+	case Message::Control: {
+		if (message->size() == 0)
+			break; // Ignore
+		auto raw = reinterpret_cast<const uint8_t *>(message->data());
+		switch (raw[0]) {
+		case MESSAGE_OPEN:
+			processOpenMessage(message);
+			break;
+		case MESSAGE_ACK:
+			if (!mIsOpen.exchange(true)) {
+				triggerOpen();
+			}
+			break;
+		case MESSAGE_CLOSE:
+			// The close message will be processed in-order in receive()
+			mRecvQueue.push(message);
+			triggerAvailable(mRecvQueue.size());
+			break;
+		default:
+			// Ignore
+			break;
+		}
+		break;
+	}
+	case Message::String:
+	case Message::Binary:
+		mRecvQueue.push(message);
+		triggerAvailable(mRecvQueue.size());
+		break;
+	default:
+		// Ignore
+		break;
+	}
+}
+
+NegotiatedDataChannel::NegotiatedDataChannel(weak_ptr<PeerConnection> pc, uint16_t stream,
+                                             string label, string protocol, Reliability reliability)
+    : DataChannel(pc, stream, std::move(label), std::move(protocol), std::move(reliability)) {}
+
+NegotiatedDataChannel::NegotiatedDataChannel(weak_ptr<PeerConnection> pc,
+                                             weak_ptr<SctpTransport> transport,
+                                             uint16_t stream)
+    : DataChannel(pc, stream, "", "", {}) {
+	mSctpTransport = transport;
+}
+
+NegotiatedDataChannel::~NegotiatedDataChannel() {}
+
+void NegotiatedDataChannel::open(shared_ptr<SctpTransport> transport) {
+	std::unique_lock lock(mMutex);
+	mSctpTransport = transport;
+
+	uint8_t channelType;
+	uint32_t reliabilityParameter;
+	switch (mReliability->type) {
+	case Reliability::Type::Rexmit:
+		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
+		reliabilityParameter = uint32_t(std::max(std::get<int>(mReliability->rexmit), 0));
+		break;
+
+	case Reliability::Type::Timed:
+		channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
+		reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
+		break;
+
+	default:
+		channelType = CHANNEL_RELIABLE;
+		reliabilityParameter = 0;
+		break;
+	}
+
+	if (mReliability->unordered)
+		channelType |= 0x80;
+
+	const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
+	binary buffer(len, byte(0));
+	auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
+	open.type = MESSAGE_OPEN;
+	open.channelType = channelType;
+	open.priority = htons(0);
+	open.reliabilityParameter = htonl(reliabilityParameter);
+	open.labelLength = htons(uint16_t(mLabel.size()));
+	open.protocolLength = htons(uint16_t(mProtocol.size()));
+
+	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
+	std::copy(mLabel.begin(), mLabel.end(), end);
+	std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
+
+	lock.unlock();
+
+	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
+}
+
+void NegotiatedDataChannel::processOpenMessage(message_ptr message) {
+	std::unique_lock lock(mMutex);
+	auto transport = mSctpTransport.lock();
+	if (!transport)
+		throw std::runtime_error("DataChannel has no transport");
+
+	if (message->size() < sizeof(OpenMessage))
+		throw std::invalid_argument("DataChannel open message too small");
+
+	OpenMessage open = *reinterpret_cast<const OpenMessage *>(message->data());
+	open.priority = ntohs(open.priority);
+	open.reliabilityParameter = ntohl(open.reliabilityParameter);
+	open.labelLength = ntohs(open.labelLength);
+	open.protocolLength = ntohs(open.protocolLength);
+
+	if (message->size() < sizeof(OpenMessage) + size_t(open.labelLength + open.protocolLength))
+		throw std::invalid_argument("DataChannel open message truncated");
+
+	auto end = reinterpret_cast<const char *>(message->data() + sizeof(OpenMessage));
+	mLabel.assign(end, open.labelLength);
+	mProtocol.assign(end + open.labelLength, open.protocolLength);
+
+	mReliability->unordered = (open.channelType & 0x80) != 0;
+	switch (open.channelType & 0x7F) {
+	case CHANNEL_PARTIAL_RELIABLE_REXMIT:
+		mReliability->type = Reliability::Type::Rexmit;
+		mReliability->rexmit = int(open.reliabilityParameter);
+		break;
+	case CHANNEL_PARTIAL_RELIABLE_TIMED:
+		mReliability->type = Reliability::Type::Timed;
+		mReliability->rexmit = milliseconds(open.reliabilityParameter);
+		break;
+	default:
+		mReliability->type = Reliability::Type::Reliable;
+		mReliability->rexmit = int(0);
+	}
+
+	lock.unlock();
+
+	binary buffer(sizeof(AckMessage), byte(0));
+	auto &ack = *reinterpret_cast<AckMessage *>(buffer.data());
+	ack.type = MESSAGE_ACK;
+
+	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
+
+	if (!mIsOpen.exchange(true))
+		triggerOpen();
+}
+
+} // namespace rtc::impl

+ 94 - 0
src/impl/datachannel.hpp

@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2019 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
+ */
+
+#ifndef RTC_IMPL_DATA_CHANNEL_H
+#define RTC_IMPL_DATA_CHANNEL_H
+
+#include "channel.hpp"
+#include "common.hpp"
+#include "message.hpp"
+#include "peerconnection.hpp"
+#include "queue.hpp"
+#include "reliability.hpp"
+#include "sctptransport.hpp"
+
+#include <atomic>
+
+namespace rtc::impl {
+
+struct PeerConnection;
+
+struct DataChannel : Channel, std::enable_shared_from_this<DataChannel> {
+	DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
+	            Reliability reliability);
+	~DataChannel();
+
+	void close();
+	void remoteClose();
+	bool outgoing(message_ptr message);
+	void incoming(message_ptr message);
+
+	optional<message_variant> receive() override;
+	optional<message_variant> peek() override;
+	size_t availableAmount() const override;
+
+	uint16_t stream() const;
+	string label() const;
+	string protocol() const;
+	Reliability reliability() const;
+
+	bool isOpen(void) const;
+	bool isClosed(void) const;
+	size_t maxMessageSize() const;
+
+	void shiftStream();
+
+	virtual void open(shared_ptr<SctpTransport> transport);
+	virtual void processOpenMessage(message_ptr);
+
+protected:
+	const weak_ptr<impl::PeerConnection> mPeerConnection;
+	weak_ptr<SctpTransport> mSctpTransport;
+
+	uint16_t mStream;
+	string mLabel;
+	string mProtocol;
+	shared_ptr<Reliability> mReliability;
+
+	mutable std::shared_mutex mMutex;
+
+	Queue<message_ptr> mRecvQueue;
+
+	std::atomic<bool> mIsOpen = false;
+	std::atomic<bool> mIsClosed = false;
+};
+
+struct NegotiatedDataChannel final : public DataChannel {
+	NegotiatedDataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
+	                      string protocol, Reliability reliability);
+	NegotiatedDataChannel(weak_ptr<PeerConnection> pc,
+	                      weak_ptr<SctpTransport> transport, uint16_t stream);
+	~NegotiatedDataChannel();
+
+	void open(impl_ptr<SctpTransport> transport) override;
+	void processOpenMessage(message_ptr message) override;
+};
+
+} // namespace rtc::impl
+
+#endif

+ 6 - 5
src/dtlssrtptransport.cpp → src/impl/dtlssrtptransport.cpp

@@ -19,17 +19,17 @@
 #include "dtlssrtptransport.hpp"
 #include "dtlssrtptransport.hpp"
 #include "logcounter.hpp"
 #include "logcounter.hpp"
 #include "tls.hpp"
 #include "tls.hpp"
+#include "rtp.hpp"
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
 #include <cstring>
 #include <cstring>
 #include <exception>
 #include <exception>
 
 
-using std::shared_ptr;
 using std::to_integer;
 using std::to_integer;
 using std::to_string;
 using std::to_string;
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 static LogCounter COUNTER_MEDIA_TRUNCATED(plog::warning,
 static LogCounter COUNTER_MEDIA_TRUNCATED(plog::warning,
                                                "Number of truncated SRT(C)P packets received");
                                                "Number of truncated SRT(C)P packets received");
@@ -48,7 +48,7 @@ static LogCounter COUNTER_SRTP_REPLAY(plog::warning, "Number of SRTP replay pack
 static LogCounter
 static LogCounter
     COUNTER_SRTP_AUTH_FAIL(plog::warning,
     COUNTER_SRTP_AUTH_FAIL(plog::warning,
                            "Number of SRTP packets received that failed authentication checks");
                            "Number of SRTP packets received that failed authentication checks");
-static rtc::LogCounter
+static LogCounter
     COUNTER_SRTP_FAIL(plog::warning,
     COUNTER_SRTP_FAIL(plog::warning,
                       "Number of SRTP packets received that had an unknown libSRTP failure");
                       "Number of SRTP packets received that had an unknown libSRTP failure");
 
 
@@ -56,12 +56,13 @@ void DtlsSrtpTransport::Init() { srtp_init(); }
 
 
 void DtlsSrtpTransport::Cleanup() { srtp_shutdown(); }
 void DtlsSrtpTransport::Cleanup() { srtp_shutdown(); }
 
 
-DtlsSrtpTransport::DtlsSrtpTransport(std::shared_ptr<IceTransport> lower,
+DtlsSrtpTransport::DtlsSrtpTransport(shared_ptr<IceTransport> lower,
                                      shared_ptr<Certificate> certificate,
                                      shared_ptr<Certificate> certificate,
+                                     optional<size_t> mtu,
                                      verifier_callback verifierCallback,
                                      verifier_callback verifierCallback,
                                      message_callback srtpRecvCallback,
                                      message_callback srtpRecvCallback,
                                      state_callback stateChangeCallback)
                                      state_callback stateChangeCallback)
-    : DtlsTransport(lower, certificate, std::move(verifierCallback),
+    : DtlsTransport(lower, certificate, mtu, std::move(verifierCallback),
                     std::move(stateChangeCallback)),
                     std::move(stateChangeCallback)),
       mSrtpRecvCallback(std::move(srtpRecvCallback)) { // distinct from Transport recv callback
       mSrtpRecvCallback(std::move(srtpRecvCallback)) { // distinct from Transport recv callback
 
 

+ 7 - 7
src/dtlssrtptransport.hpp → src/impl/dtlssrtptransport.hpp

@@ -16,11 +16,11 @@
  * 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_DTLS_SRTP_TRANSPORT_H
-#define RTC_DTLS_SRTP_TRANSPORT_H
+#ifndef RTC_IMPL_DTLS_SRTP_TRANSPORT_H
+#define RTC_IMPL_DTLS_SRTP_TRANSPORT_H
 
 
 #include "dtlstransport.hpp"
 #include "dtlstransport.hpp"
-#include "include.hpp"
+#include "common.hpp"
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
@@ -32,16 +32,16 @@
 
 
 #include <atomic>
 #include <atomic>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class DtlsSrtpTransport final : public DtlsTransport {
 class DtlsSrtpTransport final : public DtlsTransport {
 public:
 public:
 	static void Init();
 	static void Init();
 	static void Cleanup();
 	static void Cleanup();
 
 
-	DtlsSrtpTransport(std::shared_ptr<IceTransport> lower, std::shared_ptr<Certificate> certificate,
-	                  verifier_callback verifierCallback, message_callback srtpRecvCallback,
-	                  state_callback stateChangeCallback);
+	DtlsSrtpTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
+	                  optional<size_t> mtu, verifier_callback verifierCallback,
+	                  message_callback srtpRecvCallback, state_callback stateChangeCallback);
 	~DtlsSrtpTransport();
 	~DtlsSrtpTransport();
 
 
 	bool sendMedia(message_ptr message);
 	bool sendMedia(message_ptr message);

+ 23 - 20
src/dtlstransport.cpp → src/impl/dtlstransport.cpp

@@ -17,6 +17,7 @@
  */
  */
 
 
 #include "dtlstransport.hpp"
 #include "dtlstransport.hpp"
+#include "globals.hpp"
 #include "icetransport.hpp"
 #include "icetransport.hpp"
 
 
 #include <chrono>
 #include <chrono>
@@ -34,12 +35,7 @@
 
 
 using namespace std::chrono;
 using namespace std::chrono;
 
 
-using std::shared_ptr;
-using std::string;
-using std::unique_ptr;
-using std::weak_ptr;
-
-namespace rtc {
+namespace rtc::impl {
 
 
 #if USE_GNUTLS
 #if USE_GNUTLS
 
 
@@ -50,8 +46,9 @@ void DtlsTransport::Init() {
 void DtlsTransport::Cleanup() { gnutls_global_deinit(); }
 void DtlsTransport::Cleanup() { gnutls_global_deinit(); }
 
 
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
-                             verifier_callback verifierCallback, state_callback stateChangeCallback)
-    : Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
+                             optional<size_t> mtu, verifier_callback verifierCallback,
+                             state_callback stateChangeCallback)
+    : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
       mVerifierCallback(std::move(verifierCallback)),
       mVerifierCallback(std::move(verifierCallback)),
       mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
       mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
 
 
@@ -156,11 +153,15 @@ void DtlsTransport::postHandshake() {
 }
 }
 
 
 void DtlsTransport::runRecvLoop() {
 void DtlsTransport::runRecvLoop() {
-	const size_t maxMtu = 4096;
+	const size_t bufferSize = 4096;
+
 	// Handshake loop
 	// Handshake loop
 	try {
 	try {
 		changeState(State::Connecting);
 		changeState(State::Connecting);
-		gnutls_dtls_set_mtu(mSession, 1280 - 40 - 8); // min MTU over UDP/IPv6
+
+		size_t mtu = mMtu.value_or(DEFAULT_MTU) - 8 - 40; // UDP/IPv6
+		gnutls_dtls_set_mtu(mSession, static_cast<unsigned int>(mtu));
+		PLOG_VERBOSE << "SSL MTU set to " << mtu;
 
 
 		int ret;
 		int ret;
 		do {
 		do {
@@ -174,7 +175,7 @@ void DtlsTransport::runRecvLoop() {
 
 
 		// RFC 8261: DTLS MUST support sending messages larger than the current path MTU
 		// RFC 8261: DTLS MUST support sending messages larger than the current path MTU
 		// See https://tools.ietf.org/html/rfc8261#section-5
 		// See https://tools.ietf.org/html/rfc8261#section-5
-		gnutls_dtls_set_mtu(mSession, maxMtu + 1);
+		gnutls_dtls_set_mtu(mSession, bufferSize + 1);
 
 
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
 		PLOG_ERROR << "DTLS handshake: " << e.what();
 		PLOG_ERROR << "DTLS handshake: " << e.what();
@@ -188,7 +189,6 @@ void DtlsTransport::runRecvLoop() {
 		postHandshake();
 		postHandshake();
 		changeState(State::Connected);
 		changeState(State::Connected);
 
 
-		const size_t bufferSize = maxMtu;
 		char buffer[bufferSize];
 		char buffer[bufferSize];
 
 
 		while (true) {
 		while (true) {
@@ -314,8 +314,9 @@ void DtlsTransport::Cleanup() {
 }
 }
 
 
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
-                             verifier_callback verifierCallback, state_callback stateChangeCallback)
-    : Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
+                             optional<size_t> mtu, verifier_callback verifierCallback,
+                             state_callback stateChangeCallback)
+    : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
       mVerifierCallback(std::move(verifierCallback)),
       mVerifierCallback(std::move(verifierCallback)),
       mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
       mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
 	PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
 	PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
@@ -440,16 +441,18 @@ void DtlsTransport::postHandshake() {
 }
 }
 
 
 void DtlsTransport::runRecvLoop() {
 void DtlsTransport::runRecvLoop() {
-	const size_t maxMtu = 4096;
+	const size_t bufferSize = 4096;
 	try {
 	try {
 		changeState(State::Connecting);
 		changeState(State::Connecting);
-		SSL_set_mtu(mSsl, 1280 - 40 - 8); // min MTU over UDP/IPv6
+
+		size_t mtu = mMtu.value_or(DEFAULT_MTU) - 8 - 40; // UDP/IPv6
+		SSL_set_mtu(mSsl, static_cast<unsigned int>(mtu));
+		PLOG_VERBOSE << "SSL MTU set to " << mtu;
 
 
 		// Initiate the handshake
 		// Initiate the handshake
 		int ret = SSL_do_handshake(mSsl);
 		int ret = SSL_do_handshake(mSsl);
 		openssl::check(mSsl, ret, "Handshake failed");
 		openssl::check(mSsl, ret, "Handshake failed");
 
 
-		const size_t bufferSize = maxMtu;
 		byte buffer[bufferSize];
 		byte buffer[bufferSize];
 		while (mIncomingQueue.running()) {
 		while (mIncomingQueue.running()) {
 			// Process pending messages
 			// Process pending messages
@@ -466,7 +469,7 @@ void DtlsTransport::runRecvLoop() {
 					if (SSL_is_init_finished(mSsl)) {
 					if (SSL_is_init_finished(mSsl)) {
 						// RFC 8261: DTLS MUST support sending messages larger than the current path
 						// RFC 8261: DTLS MUST support sending messages larger than the current path
 						// MTU See https://tools.ietf.org/html/rfc8261#section-5
 						// MTU See https://tools.ietf.org/html/rfc8261#section-5
-						SSL_set_mtu(mSsl, maxMtu + 1);
+						SSL_set_mtu(mSsl, bufferSize + 1);
 
 
 						PLOG_INFO << "DTLS handshake finished";
 						PLOG_INFO << "DTLS handshake finished";
 						postHandshake();
 						postHandshake();
@@ -483,7 +486,7 @@ void DtlsTransport::runRecvLoop() {
 			}
 			}
 
 
 			// No more messages pending, retransmit and rearm timeout if connecting
 			// No more messages pending, retransmit and rearm timeout if connecting
-			std::optional<milliseconds> duration;
+			optional<milliseconds> duration;
 			if (state() == State::Connecting) {
 			if (state() == State::Connecting) {
 				// Warning: This function breaks the usual return value convention
 				// Warning: This function breaks the usual return value convention
 				ret = DTLSv1_handle_timeout(mSsl);
 				ret = DTLSv1_handle_timeout(mSsl);
@@ -590,4 +593,4 @@ long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /
 
 
 #endif
 #endif
 
 
-} // namespace rtc
+} // namespace rtc::impl

+ 8 - 7
src/dtlstransport.hpp → src/impl/dtlstransport.hpp

@@ -16,12 +16,11 @@
  * 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_DTLS_TRANSPORT_H
-#define RTC_DTLS_TRANSPORT_H
+#ifndef RTC_IMPL_DTLS_TRANSPORT_H
+#define RTC_IMPL_DTLS_TRANSPORT_H
 
 
 #include "certificate.hpp"
 #include "certificate.hpp"
-#include "include.hpp"
-#include "peerconnection.hpp"
+#include "common.hpp"
 #include "queue.hpp"
 #include "queue.hpp"
 #include "tls.hpp"
 #include "tls.hpp"
 #include "transport.hpp"
 #include "transport.hpp"
@@ -32,7 +31,7 @@
 #include <mutex>
 #include <mutex>
 #include <thread>
 #include <thread>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class IceTransport;
 class IceTransport;
 
 
@@ -43,8 +42,9 @@ public:
 
 
 	using verifier_callback = std::function<bool(const std::string &fingerprint)>;
 	using verifier_callback = std::function<bool(const std::string &fingerprint)>;
 
 
-	DtlsTransport(std::shared_ptr<IceTransport> lower, certificate_ptr certificate,
-	              verifier_callback verifierCallback, state_callback stateChangeCallback);
+	DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
+	              optional<size_t> mtu, verifier_callback verifierCallback,
+	              state_callback stateChangeCallback);
 	~DtlsTransport();
 	~DtlsTransport();
 
 
 	virtual void start() override;
 	virtual void start() override;
@@ -57,6 +57,7 @@ protected:
 	virtual void postHandshake();
 	virtual void postHandshake();
 	void runRecvLoop();
 	void runRecvLoop();
 
 
+	const optional<size_t> mMtu;
 	const certificate_ptr mCertificate;
 	const certificate_ptr mCertificate;
 	const verifier_callback mVerifierCallback;
 	const verifier_callback mVerifierCallback;
 	const bool mIsClient;
 	const bool mIsClient;

+ 40 - 44
src/icetransport.cpp → src/impl/icetransport.cpp

@@ -18,6 +18,7 @@
 
 
 #include "icetransport.hpp"
 #include "icetransport.hpp"
 #include "configuration.hpp"
 #include "configuration.hpp"
+#include "globals.hpp"
 #include "transport.hpp"
 #include "transport.hpp"
 
 
 #include <iostream>
 #include <iostream>
@@ -37,17 +38,14 @@
 #include <sys/types.h>
 #include <sys/types.h>
 
 
 using namespace std::chrono_literals;
 using namespace std::chrono_literals;
-
-using std::shared_ptr;
-using std::weak_ptr;
 using std::chrono::system_clock;
 using std::chrono::system_clock;
 
 
+namespace rtc::impl {
+
 #if !USE_NICE
 #if !USE_NICE
 
 
 #define MAX_TURN_SERVERS_COUNT 2
 #define MAX_TURN_SERVERS_COUNT 2
 
 
-namespace rtc {
-
 IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
 IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
                            state_callback stateChangeCallback,
                            state_callback stateChangeCallback,
                            gathering_state_callback gatheringStateChangeCallback)
                            gathering_state_callback gatheringStateChangeCallback)
@@ -103,11 +101,11 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 	// Pick a STUN server
 	// Pick a STUN server
 	for (auto &server : servers) {
 	for (auto &server : servers) {
 		if (!server.hostname.empty() && server.type == IceServer::Type::Stun) {
 		if (!server.hostname.empty() && server.type == IceServer::Type::Stun) {
-			if (server.service.empty())
-				server.service = "3478"; // STUN UDP port
-			PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.service << "\"";
+			if (server.port == 0)
+				server.port = 3478; // STUN UDP port
+			PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port << "\"";
 			jconfig.stun_server_host = server.hostname.c_str();
 			jconfig.stun_server_host = server.hostname.c_str();
-			jconfig.stun_server_port = uint16_t(std::stoul(server.service));
+			jconfig.stun_server_port = server.port;
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -119,13 +117,13 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 	int k = 0;
 	int k = 0;
 	for (auto &server : servers) {
 	for (auto &server : servers) {
 		if (!server.hostname.empty() && server.type == IceServer::Type::Turn) {
 		if (!server.hostname.empty() && server.type == IceServer::Type::Turn) {
-			if (server.service.empty())
-				server.service = "3478"; // TURN UDP port
-			PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.service << "\"";
+			if (server.port == 0)
+				server.port = 3478; // TURN UDP port
+			PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\"";
 			turn_servers[k].host = server.hostname.c_str();
 			turn_servers[k].host = server.hostname.c_str();
 			turn_servers[k].username = server.username.c_str();
 			turn_servers[k].username = server.username.c_str();
 			turn_servers[k].password = server.password.c_str();
 			turn_servers[k].password = server.password.c_str();
-			turn_servers[k].port = uint16_t(std::stoul(server.service));
+			turn_servers[k].port = server.port;
 			if (++k >= MAX_TURN_SERVERS_COUNT)
 			if (++k >= MAX_TURN_SERVERS_COUNT)
 				break;
 				break;
 		}
 		}
@@ -199,7 +197,7 @@ void IceTransport::gatherLocalCandidates(string mid) {
 	}
 	}
 }
 }
 
 
-std::optional<string> IceTransport::getLocalAddress() const {
+optional<string> IceTransport::getLocalAddress() const {
 	char str[JUICE_MAX_ADDRESS_STRING_LEN];
 	char str[JUICE_MAX_ADDRESS_STRING_LEN];
 	if (juice_get_selected_addresses(mAgent.get(), str, JUICE_MAX_ADDRESS_STRING_LEN, NULL, 0) ==
 	if (juice_get_selected_addresses(mAgent.get(), str, JUICE_MAX_ADDRESS_STRING_LEN, NULL, 0) ==
 	    0) {
 	    0) {
@@ -207,7 +205,7 @@ std::optional<string> IceTransport::getLocalAddress() const {
 	}
 	}
 	return nullopt;
 	return nullopt;
 }
 }
-std::optional<string> IceTransport::getRemoteAddress() const {
+optional<string> IceTransport::getRemoteAddress() const {
 	char str[JUICE_MAX_ADDRESS_STRING_LEN];
 	char str[JUICE_MAX_ADDRESS_STRING_LEN];
 	if (juice_get_selected_addresses(mAgent.get(), NULL, 0, str, JUICE_MAX_ADDRESS_STRING_LEN) ==
 	if (juice_get_selected_addresses(mAgent.get(), NULL, 0, str, JUICE_MAX_ADDRESS_STRING_LEN) ==
 	    0) {
 	    0) {
@@ -282,7 +280,7 @@ void IceTransport::processCandidate(const string &candidate) {
 void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
 void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
 
 
 void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
 void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
 	try {
 	try {
 		iceTransport->processStateChange(static_cast<unsigned int>(state));
 		iceTransport->processStateChange(static_cast<unsigned int>(state));
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -291,7 +289,7 @@ void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, voi
 }
 }
 
 
 void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
 void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
 	try {
 	try {
 		iceTransport->processCandidate(sdp);
 		iceTransport->processCandidate(sdp);
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -300,7 +298,7 @@ void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *use
 }
 }
 
 
 void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
 void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
 	try {
 	try {
 		iceTransport->processGatheringDone();
 		iceTransport->processGatheringDone();
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -309,7 +307,7 @@ void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
 }
 }
 
 
 void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
 void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
 	try {
 	try {
 		PLOG_VERBOSE << "Incoming size=" << size;
 		PLOG_VERBOSE << "Incoming size=" << size;
 		auto b = reinterpret_cast<const byte *>(data);
 		auto b = reinterpret_cast<const byte *>(data);
@@ -341,12 +339,8 @@ void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
 	PLOG(severity) << "juice: " << message;
 	PLOG(severity) << "juice: " << message;
 }
 }
 
 
-} // namespace rtc
-
 #else // USE_NICE == 1
 #else // USE_NICE == 1
 
 
-namespace rtc {
-
 IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
 IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
                            state_callback stateChangeCallback,
                            state_callback stateChangeCallback,
                            gathering_state_callback gatheringStateChangeCallback)
                            gathering_state_callback gatheringStateChangeCallback)
@@ -402,7 +396,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 	if (config.proxyServer.has_value()) {
 	if (config.proxyServer.has_value()) {
 		ProxyServer proxyServer = config.proxyServer.value();
 		ProxyServer proxyServer = config.proxyServer.value();
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-type", proxyServer.type, nullptr);
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-type", proxyServer.type, nullptr);
-		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-ip", proxyServer.ip.c_str(), nullptr);
+		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-ip", proxyServer.hostname.c_str(), nullptr);
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-port", proxyServer.port, nullptr);
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-port", proxyServer.port, nullptr);
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-username", proxyServer.username.c_str(),
 		g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-username", proxyServer.username.c_str(),
 		             nullptr);
 		             nullptr);
@@ -422,8 +416,8 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 			continue;
 			continue;
 		if (server.type != IceServer::Type::Stun)
 		if (server.type != IceServer::Type::Stun)
 			continue;
 			continue;
-		if (server.service.empty())
-			server.service = "3478"; // STUN UDP port
+		if (server.port == 0)
+			server.port = 3478; // STUN UDP port
 
 
 		struct addrinfo hints = {};
 		struct addrinfo hints = {};
 		hints.ai_family = AF_INET; // IPv4
 		hints.ai_family = AF_INET; // IPv4
@@ -431,9 +425,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 		hints.ai_protocol = IPPROTO_UDP;
 		hints.ai_protocol = IPPROTO_UDP;
 		hints.ai_flags = AI_ADDRCONFIG;
 		hints.ai_flags = AI_ADDRCONFIG;
 		struct addrinfo *result = nullptr;
 		struct addrinfo *result = nullptr;
-		if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
+		if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
+		                &result) != 0) {
 			PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
 			PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
-			             << server.service;
+			             << server.port;
 			continue;
 			continue;
 		}
 		}
 
 
@@ -444,7 +439,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
-					PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.service
+					PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port
 					          << "\"";
 					          << "\"";
 					g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
 					g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
 					g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port",
 					g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port",
@@ -466,8 +461,8 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 			continue;
 			continue;
 		if (server.type != IceServer::Type::Turn)
 		if (server.type != IceServer::Type::Turn)
 			continue;
 			continue;
-		if (server.service.empty())
-			server.service = server.relayType == IceServer::RelayType::TurnTls ? "5349" : "3478";
+		if (server.port == 0)
+			server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478;
 
 
 		struct addrinfo hints = {};
 		struct addrinfo hints = {};
 		hints.ai_family = AF_UNSPEC;
 		hints.ai_family = AF_UNSPEC;
@@ -477,9 +472,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 		    server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
 		    server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
 		hints.ai_flags = AI_ADDRCONFIG;
 		hints.ai_flags = AI_ADDRCONFIG;
 		struct addrinfo *result = nullptr;
 		struct addrinfo *result = nullptr;
-		if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0) {
+		if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
+		                &result) != 0) {
 			PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
 			PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
-			             << server.service;
+			             << server.port;
 			continue;
 			continue;
 		}
 		}
 
 
@@ -490,7 +486,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
-					PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.service
+					PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port
 					          << "\"";
 					          << "\"";
 					NiceRelayType niceRelayType;
 					NiceRelayType niceRelayType;
 					switch (server.relayType) {
 					switch (server.relayType) {
@@ -557,7 +553,7 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
 	g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode",
 	g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode",
 	             type == Description::Type::Offer ? TRUE : FALSE, nullptr);
 	             type == Description::Type::Offer ? TRUE : FALSE, nullptr);
 
 
-	std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
+	unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
 	                                               g_free);
 	                                               g_free);
 
 
 	// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
 	// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
@@ -616,7 +612,7 @@ void IceTransport::gatherLocalCandidates(string mid) {
 	}
 	}
 }
 }
 
 
-std::optional<string> IceTransport::getLocalAddress() const {
+optional<string> IceTransport::getLocalAddress() const {
 	NiceCandidate *local = nullptr;
 	NiceCandidate *local = nullptr;
 	NiceCandidate *remote = nullptr;
 	NiceCandidate *remote = nullptr;
 	if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
 	if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
@@ -625,7 +621,7 @@ std::optional<string> IceTransport::getLocalAddress() const {
 	return nullopt;
 	return nullopt;
 }
 }
 
 
-std::optional<string> IceTransport::getRemoteAddress() const {
+optional<string> IceTransport::getRemoteAddress() const {
 	NiceCandidate *local = nullptr;
 	NiceCandidate *local = nullptr;
 	NiceCandidate *remote = nullptr;
 	NiceCandidate *remote = nullptr;
 	if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
 	if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
@@ -715,7 +711,7 @@ string IceTransport::AddressToString(const NiceAddress &addr) {
 
 
 void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
 void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
                                      gpointer userData) {
                                      gpointer userData) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(userData);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
 	gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);
 	gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);
 	try {
 	try {
 		iceTransport->processCandidate(cand);
 		iceTransport->processCandidate(cand);
@@ -727,7 +723,7 @@ void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
 
 
 void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId*/,
 void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId*/,
                                          gpointer userData) {
                                          gpointer userData) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(userData);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
 	try {
 	try {
 		iceTransport->processGatheringDone();
 		iceTransport->processGatheringDone();
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -737,7 +733,7 @@ void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId
 
 
 void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/,
 void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/,
                                        guint /*componentId*/, guint state, gpointer userData) {
                                        guint /*componentId*/, guint state, gpointer userData) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(userData);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
 	try {
 	try {
 		iceTransport->processStateChange(state);
 		iceTransport->processStateChange(state);
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -747,7 +743,7 @@ void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/
 
 
 void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint /*componentId*/,
 void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint /*componentId*/,
                                 guint len, gchar *buf, gpointer userData) {
                                 guint len, gchar *buf, gpointer userData) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(userData);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
 	try {
 	try {
 		PLOG_VERBOSE << "Incoming size=" << len;
 		PLOG_VERBOSE << "Incoming size=" << len;
 		auto b = reinterpret_cast<byte *>(buf);
 		auto b = reinterpret_cast<byte *>(buf);
@@ -758,7 +754,7 @@ void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint
 }
 }
 
 
 gboolean IceTransport::TimeoutCallback(gpointer userData) {
 gboolean IceTransport::TimeoutCallback(gpointer userData) {
-	auto iceTransport = static_cast<rtc::IceTransport *>(userData);
+	auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
 	try {
 	try {
 		iceTransport->processTimeout();
 		iceTransport->processTimeout();
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
@@ -809,6 +805,6 @@ bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote)
 	return true;
 	return true;
 }
 }
 
 
-} // namespace rtc
-
 #endif
 #endif
+
+} // namespace rtc::impl

+ 9 - 9
src/icetransport.hpp → src/impl/icetransport.hpp

@@ -16,13 +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_ICE_TRANSPORT_H
-#define RTC_ICE_TRANSPORT_H
+#ifndef RTC_IMPL_ICE_TRANSPORT_H
+#define RTC_IMPL_ICE_TRANSPORT_H
 
 
 #include "candidate.hpp"
 #include "candidate.hpp"
 #include "configuration.hpp"
 #include "configuration.hpp"
 #include "description.hpp"
 #include "description.hpp"
-#include "include.hpp"
+#include "common.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
 #include "transport.hpp"
 #include "transport.hpp"
 
 
@@ -37,7 +37,7 @@
 #include <mutex>
 #include <mutex>
 #include <thread>
 #include <thread>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class IceTransport : public Transport {
 class IceTransport : public Transport {
 public:
 public:
@@ -58,8 +58,8 @@ public:
 	bool addRemoteCandidate(const Candidate &candidate);
 	bool addRemoteCandidate(const Candidate &candidate);
 	void gatherLocalCandidates(string mid);
 	void gatherLocalCandidates(string mid);
 
 
-	std::optional<string> getLocalAddress() const;
-	std::optional<string> getRemoteAddress() const;
+	optional<string> getLocalAddress() const;
+	optional<string> getRemoteAddress() const;
 
 
 	bool stop() override;
 	bool stop() override;
 	bool send(message_ptr message) override; // false if dropped
 	bool send(message_ptr message) override; // false if dropped
@@ -85,7 +85,7 @@ private:
 	gathering_state_callback mGatheringStateChangeCallback;
 	gathering_state_callback mGatheringStateChangeCallback;
 
 
 #if !USE_NICE
 #if !USE_NICE
-	std::unique_ptr<juice_agent_t, void (*)(juice_agent_t *)> mAgent;
+	unique_ptr<juice_agent_t, void (*)(juice_agent_t *)> mAgent;
 
 
 	static void StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr);
 	static void StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr);
 	static void CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr);
 	static void CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr);
@@ -94,8 +94,8 @@ private:
 	static void LogCallback(juice_log_level_t level, const char *message);
 	static void LogCallback(juice_log_level_t level, const char *message);
 #else
 #else
 	uint32_t mStreamId = 0;
 	uint32_t mStreamId = 0;
-	std::unique_ptr<NiceAgent, void (*)(gpointer)> mNiceAgent;
-	std::unique_ptr<GMainLoop, void (*)(GMainLoop *)> mMainLoop;
+	unique_ptr<NiceAgent, void (*)(gpointer)> mNiceAgent;
+	unique_ptr<GMainLoop, void (*)(GMainLoop *)> mMainLoop;
 	std::thread mMainLoopThread;
 	std::thread mMainLoopThread;
 	guint mTimeoutId = 0;
 	guint mTimeoutId = 0;
 	std::mutex mOutgoingMutex;
 	std::mutex mOutgoingMutex;

+ 2 - 2
src/logcounter.cpp → src/impl/logcounter.cpp

@@ -18,7 +18,7 @@
 
 
 #include "logcounter.hpp"
 #include "logcounter.hpp"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 LogCounter::LogCounter(plog::Severity severity, const std::string &text,
 LogCounter::LogCounter(plog::Severity severity, const std::string &text,
                        std::chrono::seconds duration) {
                        std::chrono::seconds duration) {
@@ -32,7 +32,7 @@ LogCounter &LogCounter::operator++(int) {
 	if (mData->mCount++ == 0) {
 	if (mData->mCount++ == 0) {
 		ThreadPool::Instance().schedule(
 		ThreadPool::Instance().schedule(
 		    mData->mDuration,
 		    mData->mDuration,
-		    [](std::weak_ptr<LogData> data) {
+		    [](weak_ptr<LogData> data) {
 			    if (auto ptr = data.lock()) {
 			    if (auto ptr = data.lock()) {
 				    int countCopy;
 				    int countCopy;
 				    countCopy = ptr->mCount.exchange(0);
 				    countCopy = ptr->mCount.exchange(0);

+ 3 - 3
src/logcounter.hpp → src/impl/logcounter.hpp

@@ -19,13 +19,13 @@
 #ifndef RTC_SERVER_LOGCOUNTER_HPP
 #ifndef RTC_SERVER_LOGCOUNTER_HPP
 #define RTC_SERVER_LOGCOUNTER_HPP
 #define RTC_SERVER_LOGCOUNTER_HPP
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "threadpool.hpp"
 #include "threadpool.hpp"
 
 
 #include <atomic>
 #include <atomic>
 #include <chrono>
 #include <chrono>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class LogCounter {
 class LogCounter {
 private:
 private:
@@ -37,7 +37,7 @@ private:
 		std::atomic<int> mCount = 0;
 		std::atomic<int> mCount = 0;
 	};
 	};
 
 
-	std::shared_ptr<LogData> mData;
+	shared_ptr<LogData> mData;
 
 
 public:
 public:
 	LogCounter(plog::Severity severity, const std::string &text,
 	LogCounter(plog::Severity severity, const std::string &text,

+ 1000 - 0
src/impl/peerconnection.cpp

@@ -0,0 +1,1000 @@
+/**
+ * Copyright (c) 2019 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 "peerconnection.hpp"
+#include "certificate.hpp"
+#include "common.hpp"
+#include "dtlstransport.hpp"
+#include "globals.hpp"
+#include "icetransport.hpp"
+#include "logcounter.hpp"
+#include "peerconnection.hpp"
+#include "processor.hpp"
+#include "rtp.hpp"
+#include "sctptransport.hpp"
+#include "threadpool.hpp"
+
+#if RTC_ENABLE_MEDIA
+#include "dtlssrtptransport.hpp"
+#endif
+
+#include <iomanip>
+#include <set>
+#include <thread>
+
+using namespace std::placeholders;
+
+namespace rtc::impl {
+
+static LogCounter COUNTER_MEDIA_TRUNCATED(plog::warning,
+                                          "Number of RTP packets truncated over past second");
+static LogCounter COUNTER_SRTP_DECRYPT_ERROR(plog::warning,
+                                             "Number of SRTP decryption errors over past second");
+static LogCounter COUNTER_SRTP_ENCRYPT_ERROR(plog::warning,
+                                             "Number of SRTP encryption errors over past second");
+static LogCounter
+    COUNTER_UNKNOWN_PACKET_TYPE(plog::warning,
+                                "Number of unknown RTCP packet types over past second");
+
+PeerConnection::PeerConnection(Configuration config_)
+    : config(std::move(config_)), mCertificate(make_certificate()),
+      mProcessor(std::make_unique<Processor>()) {
+	PLOG_VERBOSE << "Creating PeerConnection";
+
+	if (config.portRangeEnd && config.portRangeBegin > config.portRangeEnd)
+		throw std::invalid_argument("Invalid port range");
+
+	if (config.mtu) {
+		if (*config.mtu < 576) // Min MTU for IPv4
+			throw std::invalid_argument("Invalid MTU value");
+
+		if (*config.mtu > 1500) { // Standard Ethernet
+			PLOG_WARNING << "MTU set to " << *config.mtu;
+		} else {
+			PLOG_VERBOSE << "MTU set to " << *config.mtu;
+		}
+	}
+}
+
+PeerConnection::~PeerConnection() {
+	PLOG_VERBOSE << "Destroying PeerConnection";
+	mProcessor->join();
+}
+
+void PeerConnection::close() {
+	PLOG_VERBOSE << "Closing PeerConnection";
+
+	negotiationNeeded = false;
+
+	// Close data channels asynchronously
+	mProcessor->enqueue(&PeerConnection::closeDataChannels, this);
+
+	closeTransports();
+}
+
+optional<Description> PeerConnection::localDescription() const {
+	std::lock_guard lock(mLocalDescriptionMutex);
+	return mLocalDescription;
+}
+
+optional<Description> PeerConnection::remoteDescription() const {
+	std::lock_guard lock(mRemoteDescriptionMutex);
+	return mRemoteDescription;
+}
+
+shared_ptr<IceTransport> PeerConnection::initIceTransport() {
+	try {
+		if (auto transport = std::atomic_load(&mIceTransport))
+			return transport;
+
+		PLOG_VERBOSE << "Starting ICE transport";
+
+		auto transport = std::make_shared<IceTransport>(
+		    config, weak_bind(&PeerConnection::processLocalCandidate, this, _1),
+		    [this, weak_this = weak_from_this()](IceTransport::State transportState) {
+			    auto shared_this = weak_this.lock();
+			    if (!shared_this)
+				    return;
+			    switch (transportState) {
+			    case IceTransport::State::Connecting:
+				    changeState(State::Connecting);
+				    break;
+			    case IceTransport::State::Failed:
+				    changeState(State::Failed);
+				    break;
+			    case IceTransport::State::Connected:
+				    initDtlsTransport();
+				    break;
+			    case IceTransport::State::Disconnected:
+				    changeState(State::Disconnected);
+				    break;
+			    default:
+				    // Ignore
+				    break;
+			    }
+		    },
+		    [this, weak_this = weak_from_this()](IceTransport::GatheringState gatheringState) {
+			    auto shared_this = weak_this.lock();
+			    if (!shared_this)
+				    return;
+			    switch (gatheringState) {
+			    case IceTransport::GatheringState::InProgress:
+				    changeGatheringState(GatheringState::InProgress);
+				    break;
+			    case IceTransport::GatheringState::Complete:
+				    endLocalCandidates();
+				    changeGatheringState(GatheringState::Complete);
+				    break;
+			    default:
+				    // Ignore
+				    break;
+			    }
+		    });
+
+		std::atomic_store(&mIceTransport, transport);
+		if (state.load() == State::Closed) {
+			mIceTransport.reset();
+			throw std::runtime_error("Connection is closed");
+		}
+		transport->start();
+		return transport;
+
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+		changeState(State::Failed);
+		throw std::runtime_error("ICE transport initialization failed");
+	}
+}
+
+shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
+	try {
+		if (auto transport = std::atomic_load(&mDtlsTransport))
+			return transport;
+
+		PLOG_VERBOSE << "Starting DTLS transport";
+
+		auto certificate = mCertificate.get();
+		auto lower = std::atomic_load(&mIceTransport);
+		auto verifierCallback = weak_bind(&PeerConnection::checkFingerprint, this, _1);
+		auto stateChangeCallback =
+		    [this, weak_this = weak_from_this()](DtlsTransport::State transportState) {
+			    auto shared_this = weak_this.lock();
+			    if (!shared_this)
+				    return;
+
+			    switch (transportState) {
+			    case DtlsTransport::State::Connected:
+				    if (auto remote = remoteDescription(); remote && remote->hasApplication())
+					    initSctpTransport();
+				    else
+					    changeState(State::Connected);
+
+				    mProcessor->enqueue(&PeerConnection::openTracks, this);
+				    break;
+			    case DtlsTransport::State::Failed:
+				    changeState(State::Failed);
+				    break;
+			    case DtlsTransport::State::Disconnected:
+				    changeState(State::Disconnected);
+				    break;
+			    default:
+				    // Ignore
+				    break;
+			    }
+		    };
+
+		shared_ptr<DtlsTransport> transport;
+		if (auto local = localDescription(); local && local->hasAudioOrVideo()) {
+#if RTC_ENABLE_MEDIA
+			PLOG_INFO << "This connection requires media support";
+
+			// DTLS-SRTP
+			transport = std::make_shared<DtlsSrtpTransport>(
+			    lower, certificate, config.mtu, verifierCallback,
+			    weak_bind(&PeerConnection::forwardMedia, this, _1), stateChangeCallback);
+#else
+			PLOG_WARNING << "Ignoring media support (not compiled with media support)";
+#endif
+		}
+
+		if (!transport) {
+			// DTLS only
+			transport = std::make_shared<DtlsTransport>(lower, certificate, config.mtu,
+			                                            verifierCallback, stateChangeCallback);
+		}
+
+		std::atomic_store(&mDtlsTransport, transport);
+		if (state.load() == State::Closed) {
+			mDtlsTransport.reset();
+			throw std::runtime_error("Connection is closed");
+		}
+		transport->start();
+		return transport;
+
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+		changeState(State::Failed);
+		throw std::runtime_error("DTLS transport initialization failed");
+	}
+}
+
+shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
+	try {
+		if (auto transport = std::atomic_load(&mSctpTransport))
+			return transport;
+
+		PLOG_VERBOSE << "Starting SCTP transport";
+
+		auto remote = remoteDescription();
+		if (!remote || !remote->application())
+			throw std::logic_error("Starting SCTP transport without application description");
+
+		uint16_t sctpPort = remote->application()->sctpPort().value_or(DEFAULT_SCTP_PORT);
+		auto lower = std::atomic_load(&mDtlsTransport);
+		auto transport = std::make_shared<SctpTransport>(
+		    lower, sctpPort, config.mtu, weak_bind(&PeerConnection::forwardMessage, this, _1),
+		    weak_bind(&PeerConnection::forwardBufferedAmount, this, _1, _2),
+		    [this, weak_this = weak_from_this()](SctpTransport::State transportState) {
+			    auto shared_this = weak_this.lock();
+			    if (!shared_this)
+				    return;
+			    switch (transportState) {
+			    case SctpTransport::State::Connected:
+				    changeState(State::Connected);
+				    mProcessor->enqueue(&PeerConnection::openDataChannels, this);
+				    break;
+			    case SctpTransport::State::Failed:
+				    LOG_WARNING << "SCTP transport failed";
+				    changeState(State::Failed);
+				    mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
+				    break;
+			    case SctpTransport::State::Disconnected:
+				    changeState(State::Disconnected);
+				    mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
+				    break;
+			    default:
+				    // Ignore
+				    break;
+			    }
+		    });
+
+		std::atomic_store(&mSctpTransport, transport);
+		if (state.load() == State::Closed) {
+			mSctpTransport.reset();
+			throw std::runtime_error("Connection is closed");
+		}
+		transport->start();
+		return transport;
+
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+		changeState(State::Failed);
+		throw std::runtime_error("SCTP transport initialization failed");
+	}
+}
+
+shared_ptr<IceTransport> PeerConnection::getIceTransport() const {
+	return std::atomic_load(&mIceTransport);
+}
+
+shared_ptr<DtlsTransport> PeerConnection::getDtlsTransport() const {
+	return std::atomic_load(&mDtlsTransport);
+}
+
+shared_ptr<SctpTransport> PeerConnection::getSctpTransport() const {
+	return std::atomic_load(&mSctpTransport);
+}
+
+void PeerConnection::closeTransports() {
+	PLOG_VERBOSE << "Closing transports";
+
+	// Change state to sink state Closed
+	if (!changeState(State::Closed))
+		return; // already closed
+
+	// Reset callbacks now that state is changed
+	resetCallbacks();
+
+	// Initiate transport stop on the processor after closing the data channels
+	mProcessor->enqueue([this]() {
+		// Pass the pointers to a thread
+		auto sctp = std::atomic_exchange(&mSctpTransport, decltype(mSctpTransport)(nullptr));
+		auto dtls = std::atomic_exchange(&mDtlsTransport, decltype(mDtlsTransport)(nullptr));
+		auto ice = std::atomic_exchange(&mIceTransport, decltype(mIceTransport)(nullptr));
+		ThreadPool::Instance().enqueue([sctp, dtls, ice]() mutable {
+			if (sctp)
+				sctp->stop();
+			if (dtls)
+				dtls->stop();
+			if (ice)
+				ice->stop();
+
+			sctp.reset();
+			dtls.reset();
+			ice.reset();
+		});
+	});
+}
+
+void PeerConnection::endLocalCandidates() {
+	std::lock_guard lock(mLocalDescriptionMutex);
+	if (mLocalDescription)
+		mLocalDescription->endCandidates();
+}
+
+void PeerConnection::rollbackLocalDescription() {
+	PLOG_DEBUG << "Rolling back pending local description";
+
+	std::unique_lock lock(mLocalDescriptionMutex);
+	if (mCurrentLocalDescription) {
+		std::vector<Candidate> existingCandidates;
+		if (mLocalDescription)
+			existingCandidates = mLocalDescription->extractCandidates();
+
+		mLocalDescription.emplace(std::move(*mCurrentLocalDescription));
+		mLocalDescription->addCandidates(std::move(existingCandidates));
+		mCurrentLocalDescription.reset();
+	}
+}
+
+bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
+	std::lock_guard lock(mRemoteDescriptionMutex);
+	if (auto expectedFingerprint =
+	        mRemoteDescription ? mRemoteDescription->fingerprint() : nullopt) {
+		return *expectedFingerprint == fingerprint;
+	}
+	return false;
+}
+
+void PeerConnection::forwardMessage(message_ptr message) {
+	if (!message) {
+		remoteCloseDataChannels();
+		return;
+	}
+
+	uint16_t stream = uint16_t(message->stream);
+	auto channel = findDataChannel(stream);
+	if (!channel) {
+		auto iceTransport = getIceTransport();
+		auto sctpTransport = getSctpTransport();
+		if (!iceTransport || !sctpTransport)
+			return;
+
+		const byte dataChannelOpenMessage{0x03};
+		uint16_t remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
+		if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
+		    stream % 2 == remoteParity) {
+
+			channel =
+			    std::make_shared<NegotiatedDataChannel>(weak_from_this(), sctpTransport, stream);
+			channel->openCallback = weak_bind(&PeerConnection::triggerDataChannel, this,
+			                                  weak_ptr<DataChannel>{channel});
+
+			std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
+			mDataChannels.emplace(stream, channel);
+		} else {
+			// Invalid, close the DataChannel
+			sctpTransport->closeStream(message->stream);
+			return;
+		}
+	}
+
+	channel->incoming(message);
+}
+
+void PeerConnection::forwardMedia(message_ptr message) {
+	if (!message)
+		return;
+
+	// Browsers like to compound their packets with a random SSRC.
+	// we have to do this monstrosity to distribute the report blocks
+	if (message->type == Message::Control) {
+		std::set<uint32_t> ssrcs;
+		size_t offset = 0;
+		while ((sizeof(rtc::RTCP_HEADER) + offset) <= message->size()) {
+			auto header = reinterpret_cast<rtc::RTCP_HEADER *>(message->data() + offset);
+			if (header->lengthInBytes() > message->size() - offset) {
+				COUNTER_MEDIA_TRUNCATED++;
+				break;
+			}
+			offset += header->lengthInBytes();
+			if (header->payloadType() == 205 || header->payloadType() == 206) {
+				auto rtcpfb = reinterpret_cast<RTCP_FB_HEADER *>(header);
+				ssrcs.insert(rtcpfb->getPacketSenderSSRC());
+				ssrcs.insert(rtcpfb->getMediaSourceSSRC());
+
+			} else if (header->payloadType() == 200 || header->payloadType() == 201) {
+				auto rtcpsr = reinterpret_cast<RTCP_SR *>(header);
+				ssrcs.insert(rtcpsr->senderSSRC());
+				for (int i = 0; i < rtcpsr->header.reportCount(); ++i)
+					ssrcs.insert(rtcpsr->getReportBlock(i)->getSSRC());
+			} else if (header->payloadType() == 202) {
+				auto sdes = reinterpret_cast<RTCP_SDES *>(header);
+				if (!sdes->isValid()) {
+					PLOG_WARNING << "RTCP SDES packet is invalid";
+					continue;
+				}
+				for (unsigned int i = 0; i < sdes->chunksCount(); i++) {
+					auto chunk = sdes->getChunk(i);
+					ssrcs.insert(chunk->ssrc());
+				}
+			} else {
+				// PT=207 == Extended Report
+				if (header->payloadType() != 207) {
+					COUNTER_UNKNOWN_PACKET_TYPE++;
+				}
+			}
+		}
+
+		if (!ssrcs.empty()) {
+			for (uint32_t ssrc : ssrcs) {
+				if (auto mid = getMidFromSsrc(ssrc)) {
+					std::shared_lock lock(mTracksMutex); // read-only
+					if (auto it = mTracks.find(*mid); it != mTracks.end())
+						if (auto track = it->second.lock())
+							track->incoming(message);
+				}
+			}
+			return;
+		}
+	}
+
+	uint32_t ssrc = uint32_t(message->stream);
+	if (auto mid = getMidFromSsrc(ssrc)) {
+		std::shared_lock lock(mTracksMutex); // read-only
+		if (auto it = mTracks.find(*mid); it != mTracks.end())
+			if (auto track = it->second.lock())
+				track->incoming(message);
+	} else {
+		/*
+		 * TODO: So the problem is that when stop sending streams, we stop getting report blocks for
+		 * those streams Therefore when we get compound RTCP packets, they are empty, and we can't
+		 * forward them. Therefore, it is expected that we don't know where to forward packets. Is
+		 * this ideal? No! Do I know how to fix it? No!
+		 */
+		// PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
+		return;
+	}
+}
+
+optional<std::string> PeerConnection::getMidFromSsrc(uint32_t ssrc) {
+	if (auto it = mMidFromSsrc.find(ssrc); it != mMidFromSsrc.end())
+		return it->second;
+
+	{
+		std::lock_guard lock(mRemoteDescriptionMutex);
+		if (!mRemoteDescription)
+			return nullopt;
+		for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
+			if (auto found = std::visit(
+			        rtc::overloaded{[&](Description::Application *) -> optional<string> {
+				                        return std::nullopt;
+			                        },
+			                        [&](Description::Media *media) -> optional<string> {
+				                        return media->hasSSRC(ssrc)
+				                                   ? std::make_optional(media->mid())
+				                                   : nullopt;
+			                        }},
+			        mRemoteDescription->media(i))) {
+
+				mMidFromSsrc.emplace(ssrc, *found);
+				return *found;
+			}
+		}
+	}
+	{
+		std::lock_guard lock(mLocalDescriptionMutex);
+		if (!mLocalDescription)
+			return nullopt;
+		for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
+			if (auto found = std::visit(
+			        rtc::overloaded{[&](Description::Application *) -> optional<string> {
+				                        return std::nullopt;
+			                        },
+			                        [&](Description::Media *media) -> optional<string> {
+				                        return media->hasSSRC(ssrc)
+				                                   ? std::make_optional(media->mid())
+				                                   : nullopt;
+			                        }},
+			        mLocalDescription->media(i))) {
+
+				mMidFromSsrc.emplace(ssrc, *found);
+				return *found;
+			}
+		}
+	}
+
+	return nullopt;
+}
+
+void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
+	if (auto channel = findDataChannel(stream))
+		channel->triggerBufferedAmount(amount);
+}
+
+shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role, string label,
+                                                           DataChannelInit init) {
+	std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
+	uint16_t stream;
+	if (init.id) {
+		stream = *init.id;
+		if (stream == 65535)
+			throw std::invalid_argument("Invalid DataChannel id");
+	} else {
+		// The active side must use streams with even identifiers, whereas the passive side must use
+		// streams with odd identifiers.
+		// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
+		stream = (role == Description::Role::Active) ? 0 : 1;
+		while (mDataChannels.find(stream) != mDataChannels.end()) {
+			if (stream >= 65535 - 2)
+				throw std::runtime_error("Too many DataChannels");
+
+			stream += 2;
+		}
+	}
+	// If the DataChannel is user-negotiated, do not negociate it here
+	auto channel =
+	    init.negotiated
+	        ? std::make_shared<DataChannel>(weak_from_this(), stream, std::move(label),
+	                                        std::move(init.protocol), std::move(init.reliability))
+	        : std::make_shared<NegotiatedDataChannel>(weak_from_this(), stream, std::move(label),
+	                                                  std::move(init.protocol),
+	                                                  std::move(init.reliability));
+	mDataChannels.emplace(std::make_pair(stream, channel));
+	return channel;
+}
+
+shared_ptr<DataChannel> PeerConnection::findDataChannel(uint16_t stream) {
+	std::shared_lock lock(mDataChannelsMutex); // read-only
+	if (auto it = mDataChannels.find(stream); it != mDataChannels.end())
+		if (auto channel = it->second.lock())
+			return channel;
+
+	return nullptr;
+}
+
+void PeerConnection::shiftDataChannels() {
+	auto iceTransport = std::atomic_load(&mIceTransport);
+	auto sctpTransport = std::atomic_load(&mSctpTransport);
+	if (!sctpTransport && iceTransport && iceTransport->role() == Description::Role::Active) {
+		std::unique_lock lock(mDataChannelsMutex); // we are going to swap the container
+		decltype(mDataChannels) newDataChannels;
+		auto it = mDataChannels.begin();
+		while (it != mDataChannels.end()) {
+			auto channel = it->second.lock();
+			channel->shiftStream();
+			newDataChannels.emplace(channel->stream(), channel);
+			++it;
+		}
+		std::swap(mDataChannels, newDataChannels);
+	}
+}
+
+void PeerConnection::iterateDataChannels(
+    std::function<void(shared_ptr<DataChannel> channel)> func) {
+	// Iterate
+	{
+		std::shared_lock lock(mDataChannelsMutex); // read-only
+		auto it = mDataChannels.begin();
+		while (it != mDataChannels.end()) {
+			auto channel = it->second.lock();
+			if (channel && !channel->isClosed())
+				func(channel);
+
+			++it;
+		}
+	}
+
+	// Cleanup
+	{
+		std::unique_lock lock(mDataChannelsMutex); // we are going to erase
+		auto it = mDataChannels.begin();
+		while (it != mDataChannels.end()) {
+			if (!it->second.lock()) {
+				it = mDataChannels.erase(it);
+				continue;
+			}
+
+			++it;
+		}
+	}
+}
+
+void PeerConnection::openDataChannels() {
+	if (auto transport = std::atomic_load(&mSctpTransport))
+		iterateDataChannels([&](shared_ptr<DataChannel> channel) { channel->open(transport); });
+}
+
+void PeerConnection::closeDataChannels() {
+	iterateDataChannels([&](shared_ptr<DataChannel> channel) { channel->close(); });
+}
+
+void PeerConnection::remoteCloseDataChannels() {
+	iterateDataChannels([&](shared_ptr<DataChannel> channel) { channel->remoteClose(); });
+}
+
+shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
+	shared_ptr<Track> track;
+	if (auto it = mTracks.find(description.mid()); it != mTracks.end())
+		if (track = it->second.lock(); track)
+			track->setDescription(std::move(description));
+
+	if (!track) {
+		track = std::make_shared<Track>(weak_from_this(), std::move(description));
+		mTracks.emplace(std::make_pair(track->mid(), track));
+		mTrackLines.emplace_back(track);
+	}
+
+	return track;
+}
+
+void PeerConnection::incomingTrack(Description::Media description) {
+	std::unique_lock lock(mTracksMutex); // we are going to emplace
+#if !RTC_ENABLE_MEDIA
+	if (mTracks.empty()) {
+		PLOG_WARNING << "Tracks will be inative (not compiled with media support)";
+	}
+#endif
+	if (mTracks.find(description.mid()) == mTracks.end()) {
+		auto track = std::make_shared<Track>(weak_from_this(), std::move(description));
+		mTracks.emplace(std::make_pair(track->mid(), track));
+		mTrackLines.emplace_back(track);
+		triggerTrack(track);
+	}
+}
+
+void PeerConnection::openTracks() {
+#if RTC_ENABLE_MEDIA
+	if (auto transport = std::atomic_load(&mDtlsTransport)) {
+		auto srtpTransport = std::dynamic_pointer_cast<DtlsSrtpTransport>(transport);
+		std::shared_lock lock(mTracksMutex); // read-only
+		for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
+			if (auto track = it->second.lock())
+				if (!track->isOpen())
+					track->open(srtpTransport);
+	}
+#endif
+}
+
+void PeerConnection::validateRemoteDescription(const Description &description) {
+	if (!description.iceUfrag())
+		throw std::invalid_argument("Remote description has no ICE user fragment");
+
+	if (!description.icePwd())
+		throw std::invalid_argument("Remote description has no ICE password");
+
+	if (!description.fingerprint())
+		throw std::invalid_argument("Remote description has no fingerprint");
+
+	if (description.mediaCount() == 0)
+		throw std::invalid_argument("Remote description has no media line");
+
+	int activeMediaCount = 0;
+	for (unsigned int i = 0; i < description.mediaCount(); ++i)
+		std::visit(rtc::overloaded{[&](const Description::Application *) { ++activeMediaCount; },
+		                           [&](const Description::Media *media) {
+			                           if (media->direction() != Description::Direction::Inactive)
+				                           ++activeMediaCount;
+		                           }},
+		           description.media(i));
+
+	if (activeMediaCount == 0)
+		throw std::invalid_argument("Remote description has no active media");
+
+	if (auto local = localDescription(); local && local->iceUfrag() && local->icePwd())
+		if (*description.iceUfrag() == *local->iceUfrag() &&
+		    *description.icePwd() == *local->icePwd())
+			throw std::logic_error("Got the local description as remote description");
+
+	PLOG_VERBOSE << "Remote description looks valid";
+}
+
+void PeerConnection::processLocalDescription(Description description) {
+
+	if (auto remote = remoteDescription()) {
+		// Reciprocate remote description
+		for (unsigned int i = 0; i < remote->mediaCount(); ++i)
+			std::visit( // reciprocate each media
+			    rtc::overloaded{
+			        [&](Description::Application *remoteApp) {
+				        std::shared_lock lock(mDataChannelsMutex);
+				        if (!mDataChannels.empty()) {
+					        // Prefer local description
+					        Description::Application app(remoteApp->mid());
+					        app.setSctpPort(DEFAULT_SCTP_PORT);
+					        app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
+
+					        PLOG_DEBUG << "Adding application to local description, mid=\""
+					                   << app.mid() << "\"";
+
+					        description.addMedia(std::move(app));
+					        return;
+				        }
+
+				        auto reciprocated = remoteApp->reciprocate();
+				        reciprocated.hintSctpPort(DEFAULT_SCTP_PORT);
+				        reciprocated.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
+
+				        PLOG_DEBUG << "Reciprocating application in local description, mid=\""
+				                   << reciprocated.mid() << "\"";
+
+				        description.addMedia(std::move(reciprocated));
+			        },
+			        [&](Description::Media *remoteMedia) {
+				        std::shared_lock lock(mTracksMutex);
+				        if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end()) {
+					        // Prefer local description
+					        if (auto track = it->second.lock()) {
+						        auto media = track->description();
+#if !RTC_ENABLE_MEDIA
+						        // No media support, mark as inactive
+						        media.setDirection(Description::Direction::Inactive);
+#endif
+						        PLOG_DEBUG
+						            << "Adding media to local description, mid=\"" << media.mid()
+						            << "\", active=" << std::boolalpha
+						            << (media.direction() != Description::Direction::Inactive);
+
+						        description.addMedia(std::move(media));
+					        } else {
+						        auto reciprocated = remoteMedia->reciprocate();
+						        reciprocated.setDirection(Description::Direction::Inactive);
+
+						        PLOG_DEBUG << "Adding inactive media to local description, mid=\""
+						                   << reciprocated.mid() << "\"";
+
+						        description.addMedia(std::move(reciprocated));
+					        }
+					        return;
+				        }
+				        lock.unlock(); // we are going to call incomingTrack()
+
+				        auto reciprocated = remoteMedia->reciprocate();
+#if !RTC_ENABLE_MEDIA
+				        // No media support, mark as inactive
+				        reciprocated.setDirection(Description::Direction::Inactive);
+#endif
+				        incomingTrack(reciprocated);
+
+				        PLOG_DEBUG
+				            << "Reciprocating media in local description, mid=\""
+				            << reciprocated.mid() << "\", active=" << std::boolalpha
+				            << (reciprocated.direction() != Description::Direction::Inactive);
+
+				        description.addMedia(std::move(reciprocated));
+			        },
+			    },
+			    remote->media(i));
+	}
+
+	if (description.type() == Description::Type::Offer) {
+		// This is an offer, add locally created data channels and tracks
+		// Add application for data channels
+		if (!description.hasApplication()) {
+			std::shared_lock lock(mDataChannelsMutex);
+			if (!mDataChannels.empty()) {
+				unsigned int m = 0;
+				while (description.hasMid(std::to_string(m)))
+					++m;
+				Description::Application app(std::to_string(m));
+				app.setSctpPort(DEFAULT_SCTP_PORT);
+				app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
+
+				PLOG_DEBUG << "Adding application to local description, mid=\"" << app.mid()
+				           << "\"";
+
+				description.addMedia(std::move(app));
+			}
+		}
+
+		// Add media for local tracks
+		std::shared_lock lock(mTracksMutex);
+		for (auto it = mTrackLines.begin(); it != mTrackLines.end(); ++it) {
+			if (auto track = it->lock()) {
+				if (description.hasMid(track->mid()))
+					continue;
+
+				auto media = track->description();
+#if !RTC_ENABLE_MEDIA
+				// No media support, mark as inactive
+				media.setDirection(Description::Direction::Inactive);
+#endif
+				PLOG_DEBUG << "Adding media to local description, mid=\"" << media.mid()
+				           << "\", active=" << std::boolalpha
+				           << (media.direction() != Description::Direction::Inactive);
+
+				description.addMedia(std::move(media));
+			}
+		}
+	}
+
+	// Set local fingerprint (wait for certificate if necessary)
+	description.setFingerprint(mCertificate.get()->fingerprint());
+
+	{
+		// Set as local description
+		std::lock_guard lock(mLocalDescriptionMutex);
+
+		std::vector<Candidate> existingCandidates;
+		if (mLocalDescription) {
+			existingCandidates = mLocalDescription->extractCandidates();
+			mCurrentLocalDescription.emplace(std::move(*mLocalDescription));
+		}
+
+		mLocalDescription.emplace(description);
+		mLocalDescription->addCandidates(std::move(existingCandidates));
+	}
+
+	PLOG_VERBOSE << "Issuing local description: " << description;
+	mProcessor->enqueue(localDescriptionCallback.wrap(), std::move(description));
+
+	// Reciprocated tracks might need to be open
+	if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
+	    dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
+		mProcessor->enqueue(&PeerConnection::openTracks, this);
+}
+
+void PeerConnection::processLocalCandidate(Candidate candidate) {
+	std::lock_guard lock(mLocalDescriptionMutex);
+	if (!mLocalDescription)
+		throw std::logic_error("Got a local candidate without local description");
+
+	candidate.resolve(Candidate::ResolveMode::Simple);
+	mLocalDescription->addCandidate(candidate);
+
+	PLOG_VERBOSE << "Issuing local candidate: " << candidate;
+	mProcessor->enqueue(localCandidateCallback.wrap(), std::move(candidate));
+}
+
+void PeerConnection::processRemoteDescription(Description description) {
+	{
+		// Set as remote description
+		std::lock_guard lock(mRemoteDescriptionMutex);
+
+		std::vector<Candidate> existingCandidates;
+		if (mRemoteDescription)
+			existingCandidates = mRemoteDescription->extractCandidates();
+
+		mRemoteDescription.emplace(description);
+		mRemoteDescription->addCandidates(std::move(existingCandidates));
+	}
+
+	auto iceTransport = initIceTransport();
+	iceTransport->setRemoteDescription(std::move(description));
+
+	if (description.hasApplication()) {
+		auto dtlsTransport = std::atomic_load(&mDtlsTransport);
+		auto sctpTransport = std::atomic_load(&mSctpTransport);
+		if (!sctpTransport && dtlsTransport &&
+		    dtlsTransport->state() == Transport::State::Connected)
+			initSctpTransport();
+	}
+}
+
+void PeerConnection::processRemoteCandidate(Candidate candidate) {
+	auto iceTransport = std::atomic_load(&mIceTransport);
+	{
+		// Set as remote candidate
+		std::lock_guard lock(mRemoteDescriptionMutex);
+		if (!mRemoteDescription)
+			throw std::logic_error("Got a remote candidate without remote description");
+
+		if (!iceTransport)
+			throw std::logic_error("Got a remote candidate without ICE transport");
+
+		candidate.hintMid(mRemoteDescription->bundleMid());
+
+		if (mRemoteDescription->hasCandidate(candidate))
+			return; // already in description, ignore
+
+		candidate.resolve(Candidate::ResolveMode::Simple);
+		mRemoteDescription->addCandidate(candidate);
+	}
+
+	if (candidate.isResolved()) {
+		iceTransport->addRemoteCandidate(std::move(candidate));
+	} else {
+		// We might need a lookup, do it asynchronously
+		// We don't use the thread pool because we have no control on the timeout
+		if ((iceTransport = std::atomic_load(&mIceTransport))) {
+			weak_ptr<IceTransport> weakIceTransport{iceTransport};
+			std::thread t([weakIceTransport, candidate = std::move(candidate)]() mutable {
+				if (candidate.resolve(Candidate::ResolveMode::Lookup))
+					if (auto iceTransport = weakIceTransport.lock())
+						iceTransport->addRemoteCandidate(std::move(candidate));
+			});
+			t.detach();
+		}
+	}
+}
+
+string PeerConnection::localBundleMid() const {
+	std::lock_guard lock(mLocalDescriptionMutex);
+	return mLocalDescription ? mLocalDescription->bundleMid() : "0";
+}
+
+void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
+	auto dataChannel = weakDataChannel.lock();
+	if (!dataChannel)
+		return;
+
+	mProcessor->enqueue(dataChannelCallback.wrap(),
+	                    std::make_shared<rtc::DataChannel>(std::move(dataChannel)));
+}
+
+void PeerConnection::triggerTrack(shared_ptr<Track> track) {
+	mProcessor->enqueue(trackCallback.wrap(), std::make_shared<rtc::Track>(std::move(track)));
+}
+
+bool PeerConnection::changeState(State newState) {
+	State current;
+	do {
+		current = state.load();
+		if (current == State::Closed)
+			return false;
+		if (current == newState)
+			return false;
+
+	} while (!state.compare_exchange_weak(current, newState));
+
+	std::ostringstream s;
+	s << newState;
+	PLOG_INFO << "Changed state to " << s.str();
+
+	if (newState == State::Closed)
+		// This is the last state change, so we may steal the callback
+		mProcessor->enqueue([cb = std::move(stateChangeCallback)]() { cb(State::Closed); });
+	else
+		mProcessor->enqueue(stateChangeCallback.wrap(), newState);
+
+	return true;
+}
+
+bool PeerConnection::changeGatheringState(GatheringState newState) {
+	if (gatheringState.exchange(newState) == newState)
+		return false;
+
+	std::ostringstream s;
+	s << newState;
+	PLOG_INFO << "Changed gathering state to " << s.str();
+	mProcessor->enqueue(gatheringStateChangeCallback.wrap(), newState);
+	return true;
+}
+
+bool PeerConnection::changeSignalingState(SignalingState newState) {
+	if (signalingState.exchange(newState) == newState)
+		return false;
+
+	std::ostringstream s;
+	s << state;
+	PLOG_INFO << "Changed signaling state to " << s.str();
+	mProcessor->enqueue(signalingStateChangeCallback.wrap(), newState);
+	return true;
+}
+
+void PeerConnection::resetCallbacks() {
+	// Unregister all callbacks
+	dataChannelCallback = nullptr;
+	localDescriptionCallback = nullptr;
+	localCandidateCallback = nullptr;
+	stateChangeCallback = nullptr;
+	gatheringStateChangeCallback = nullptr;
+}
+
+} // namespace rtc::impl

+ 134 - 0
src/impl/peerconnection.hpp

@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2019-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
+ */
+
+#ifndef RTC_IMPL_PEER_CONNECTION_H
+#define RTC_IMPL_PEER_CONNECTION_H
+
+#include "common.hpp"
+#include "datachannel.hpp"
+#include "dtlstransport.hpp"
+#include "icetransport.hpp"
+#include "sctptransport.hpp"
+#include "track.hpp"
+
+#include "rtc/peerconnection.hpp"
+
+#include <mutex>
+#include <shared_mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace rtc::impl {
+
+struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
+	using State = rtc::PeerConnection::State;
+	using GatheringState = rtc::PeerConnection::GatheringState;
+	using SignalingState = rtc::PeerConnection::SignalingState;
+
+	PeerConnection(Configuration config_);
+	~PeerConnection();
+
+	void close();
+
+	optional<Description> localDescription() const;
+	optional<Description> remoteDescription() const;
+
+	shared_ptr<IceTransport> initIceTransport();
+	shared_ptr<DtlsTransport> initDtlsTransport();
+	shared_ptr<SctpTransport> initSctpTransport();
+	shared_ptr<IceTransport> getIceTransport() const;
+	shared_ptr<DtlsTransport> getDtlsTransport() const;
+	shared_ptr<SctpTransport> getSctpTransport() const;
+	void closeTransports();
+
+	void endLocalCandidates();
+	void rollbackLocalDescription();
+	bool checkFingerprint(const std::string &fingerprint) const;
+	void forwardMessage(message_ptr message);
+	void forwardMedia(message_ptr message);
+	void forwardBufferedAmount(uint16_t stream, size_t amount);
+	optional<string> getMidFromSsrc(uint32_t ssrc);
+
+	shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
+	                                           DataChannelInit init);
+	shared_ptr<DataChannel> findDataChannel(uint16_t stream);
+	void shiftDataChannels();
+	void iterateDataChannels(std::function<void(shared_ptr<DataChannel> channel)> func);
+	void openDataChannels();
+	void closeDataChannels();
+	void remoteCloseDataChannels();
+
+	shared_ptr<Track> emplaceTrack(Description::Media description);
+	void incomingTrack(Description::Media description);
+	void openTracks();
+
+	void validateRemoteDescription(const Description &description);
+	void processLocalDescription(Description description);
+	void processLocalCandidate(Candidate candidate);
+	void processRemoteDescription(Description description);
+	void processRemoteCandidate(Candidate candidate);
+	string localBundleMid() const;
+
+	void triggerDataChannel(weak_ptr<DataChannel> weakDataChannel);
+	void triggerTrack(shared_ptr<Track> track);
+	bool changeState(State newState);
+	bool changeGatheringState(GatheringState newState);
+	bool changeSignalingState(SignalingState newState);
+
+	void resetCallbacks();
+
+	void outgoingMedia(message_ptr message);
+
+	const Configuration config;
+	std::atomic<State> state = State::New;
+	std::atomic<GatheringState> gatheringState = GatheringState::New;
+	std::atomic<SignalingState> signalingState = SignalingState::Stable;
+	std::atomic<bool> negotiationNeeded = false;
+
+	synchronized_callback<shared_ptr<rtc::DataChannel>> dataChannelCallback;
+	synchronized_callback<Description> localDescriptionCallback;
+	synchronized_callback<Candidate> localCandidateCallback;
+	synchronized_callback<State> stateChangeCallback;
+	synchronized_callback<GatheringState> gatheringStateChangeCallback;
+	synchronized_callback<SignalingState> signalingStateChangeCallback;
+	synchronized_callback<shared_ptr<rtc::Track>> trackCallback;
+
+private:
+	const init_token mInitToken = Init::Token();
+	const future_certificate_ptr mCertificate;
+	const unique_ptr<Processor> mProcessor;
+
+	optional<Description> mLocalDescription, mRemoteDescription;
+	optional<Description> mCurrentLocalDescription;
+	mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
+
+	shared_ptr<IceTransport> mIceTransport;
+	shared_ptr<DtlsTransport> mDtlsTransport;
+	shared_ptr<SctpTransport> mSctpTransport;
+
+	std::unordered_map<uint16_t, weak_ptr<DataChannel>> mDataChannels; // by stream ID
+	std::unordered_map<string, weak_ptr<Track>> mTracks;               // by mid
+	std::vector<weak_ptr<Track>> mTrackLines;                          // by SDP order
+	std::shared_mutex mDataChannelsMutex, mTracksMutex;
+
+	std::unordered_map<uint32_t, string> mMidFromSsrc; // cache
+};
+
+} // namespace rtc::impl
+
+#endif

+ 2 - 2
src/processor.cpp → src/impl/processor.cpp

@@ -18,7 +18,7 @@
 
 
 #include "processor.hpp"
 #include "processor.hpp"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 Processor::Processor(size_t limit) : mTasks(limit) {}
 Processor::Processor(size_t limit) : mTasks(limit) {}
 
 
@@ -40,4 +40,4 @@ void Processor::schedule() {
 	}
 	}
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl

+ 5 - 5
src/processor.hpp → src/impl/processor.hpp

@@ -16,10 +16,10 @@
  * 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_PROCESSOR_H
-#define RTC_PROCESSOR_H
+#ifndef RTC_IMPL_PROCESSOR_H
+#define RTC_IMPL_PROCESSOR_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "init.hpp"
 #include "init.hpp"
 #include "queue.hpp"
 #include "queue.hpp"
 #include "threadpool.hpp"
 #include "threadpool.hpp"
@@ -30,7 +30,7 @@
 #include <mutex>
 #include <mutex>
 #include <queue>
 #include <queue>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 // Processed tasks in order by delegating them to the thread pool
 // Processed tasks in order by delegating them to the thread pool
 class Processor final {
 class Processor final {
@@ -76,6 +76,6 @@ template <class F, class... Args> void Processor::enqueue(F &&f, Args &&...args)
 	}
 	}
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 18 - 19
include/rtc/queue.hpp → src/impl/queue.hpp

@@ -16,19 +16,18 @@
  * 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_QUEUE_H
-#define RTC_QUEUE_H
+#ifndef RTC_IMPL_QUEUE_H
+#define RTC_IMPL_QUEUE_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 #include <atomic>
 #include <atomic>
 #include <chrono>
 #include <chrono>
 #include <condition_variable>
 #include <condition_variable>
 #include <mutex>
 #include <mutex>
-#include <optional>
 #include <queue>
 #include <queue>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 template <typename T> class Queue {
 template <typename T> class Queue {
 public:
 public:
@@ -44,15 +43,15 @@ public:
 	size_t size() const;   // elements
 	size_t size() const;   // elements
 	size_t amount() const; // amount
 	size_t amount() const; // amount
 	void push(T element);
 	void push(T element);
-	std::optional<T> pop();
-	std::optional<T> tryPop();
-	std::optional<T> peek();
-	std::optional<T> exchange(T element);
-	bool wait(const std::optional<std::chrono::milliseconds> &duration = nullopt);
+	optional<T> pop();
+	optional<T> tryPop();
+	optional<T> peek();
+	optional<T> exchange(T element);
+	bool wait(const optional<std::chrono::milliseconds> &duration = nullopt);
 
 
 private:
 private:
 	void pushImpl(T element);
 	void pushImpl(T element);
-	std::optional<T> popImpl();
+	optional<T> popImpl();
 
 
 	const size_t mLimit;
 	const size_t mLimit;
 	size_t mAmount;
 	size_t mAmount;
@@ -112,23 +111,23 @@ template <typename T> void Queue<T>::push(T element) {
 	pushImpl(std::move(element));
 	pushImpl(std::move(element));
 }
 }
 
 
-template <typename T> std::optional<T> Queue<T>::pop() {
+template <typename T> optional<T> Queue<T>::pop() {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
 	mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
 	mPopCondition.wait(lock, [this]() { return !mQueue.empty() || mStopping; });
 	return popImpl();
 	return popImpl();
 }
 }
 
 
-template <typename T> std::optional<T> Queue<T>::tryPop() {
+template <typename T> optional<T> Queue<T>::tryPop() {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
 	return popImpl();
 	return popImpl();
 }
 }
 
 
-template <typename T> std::optional<T> Queue<T>::peek() {
+template <typename T> optional<T> Queue<T>::peek() {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
 	return !mQueue.empty() ? std::make_optional(mQueue.front()) : nullopt;
 	return !mQueue.empty() ? std::make_optional(mQueue.front()) : nullopt;
 }
 }
 
 
-template <typename T> std::optional<T> Queue<T>::exchange(T element) {
+template <typename T> optional<T> Queue<T>::exchange(T element) {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
 	if (mQueue.empty())
 	if (mQueue.empty())
 		return nullopt;
 		return nullopt;
@@ -138,7 +137,7 @@ template <typename T> std::optional<T> Queue<T>::exchange(T element) {
 }
 }
 
 
 template <typename T>
 template <typename T>
-bool Queue<T>::wait(const std::optional<std::chrono::milliseconds> &duration) {
+bool Queue<T>::wait(const optional<std::chrono::milliseconds> &duration) {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
 	if (duration)
 	if (duration)
 		mPopCondition.wait_for(lock, *duration, [this]() { return !mQueue.empty() || mStopping; });
 		mPopCondition.wait_for(lock, *duration, [this]() { return !mQueue.empty() || mStopping; });
@@ -157,16 +156,16 @@ template <typename T> void Queue<T>::pushImpl(T element) {
 	mPopCondition.notify_one();
 	mPopCondition.notify_one();
 }
 }
 
 
-template <typename T> std::optional<T> Queue<T>::popImpl() {
+template <typename T> optional<T> Queue<T>::popImpl() {
 	if (mQueue.empty())
 	if (mQueue.empty())
 		return nullopt;
 		return nullopt;
 
 
 	mAmount -= mAmountFunction(mQueue.front());
 	mAmount -= mAmountFunction(mQueue.front());
-	std::optional<T> element{std::move(mQueue.front())};
+	optional<T> element{std::move(mQueue.front())};
 	mQueue.pop();
 	mQueue.pop();
 	return element;
 	return element;
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 69 - 43
src/sctptransport.cpp → src/impl/sctptransport.cpp

@@ -17,6 +17,8 @@
  */
  */
 
 
 #include "sctptransport.hpp"
 #include "sctptransport.hpp"
+#include "dtlstransport.hpp"
+#include "globals.hpp"
 #include "logcounter.hpp"
 #include "logcounter.hpp"
 
 
 #include <chrono>
 #include <chrono>
@@ -27,8 +29,7 @@
 
 
 // The IETF draft says:
 // The IETF draft says:
 // SCTP MUST support performing Path MTU discovery without relying on ICMP or ICMPv6 as specified in
 // SCTP MUST support performing Path MTU discovery without relying on ICMP or ICMPv6 as specified in
-// [RFC4821] using probing messages specified in [RFC4820]. The initial Path MTU at the IP layer
-// SHOULD NOT exceed 1200 bytes for IPv4 and 1280 for IPv6.
+// [RFC4821] using probing messages specified in [RFC4820].
 // See https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-5
 // See https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-5
 //
 //
 // However, usrsctp does not implement Path MTU discovery, so we need to disable it for now.
 // However, usrsctp does not implement Path MTU discovery, so we need to disable it for now.
@@ -54,9 +55,7 @@
 using namespace std::chrono_literals;
 using namespace std::chrono_literals;
 using namespace std::chrono;
 using namespace std::chrono;
 
 
-using std::shared_ptr;
-
-namespace rtc {
+namespace rtc::impl {
 
 
 static LogCounter COUNTER_UNKNOWN_PPID(plog::warning,
 static LogCounter COUNTER_UNKNOWN_PPID(plog::warning,
                                        "Number of SCTP packets received with an unknown PPID");
                                        "Number of SCTP packets received with an unknown PPID");
@@ -83,11 +82,12 @@ void SctpTransport::Init() {
 
 
 	usrsctp_sysctl_set_sctp_max_chunks_on_queue(10 * 1024);
 	usrsctp_sysctl_set_sctp_max_chunks_on_queue(10 * 1024);
 
 
-	// Change congestion control from the default TCP Reno (RFC 2581) to H-TCP
-	usrsctp_sysctl_set_sctp_default_cc_module(SCTP_CC_HTCP);
+	// Use default congestion control (RFC 4960)
+	// See https://github.com/paullouisageneau/libdatachannel/issues/354
+	usrsctp_sysctl_set_sctp_default_cc_module(0);
 
 
-	// Enable Non-Renegable Selective Acknowledgments (NR-SACKs)
-	usrsctp_sysctl_set_sctp_nrsack_enable(1);
+	// Enable Partial Reliability Extension (RFC 3758)
+	usrsctp_sysctl_set_sctp_pr_enable(1);
 
 
 	// Increase the initial window size to 10 MTUs (RFC 6928)
 	// Increase the initial window size to 10 MTUs (RFC 6928)
 	usrsctp_sysctl_set_sctp_initial_cwnd(10);
 	usrsctp_sysctl_set_sctp_initial_cwnd(10);
@@ -101,10 +101,11 @@ void SctpTransport::Cleanup() {
 		std::this_thread::sleep_for(100ms);
 		std::this_thread::sleep_for(100ms);
 }
 }
 
 
-SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
-                             message_callback recvCallback, amount_callback bufferedAmountCallback,
+SctpTransport::SctpTransport(shared_ptr<Transport> lower, uint16_t port,
+                             optional<size_t> mtu, message_callback recvCallback,
+                             amount_callback bufferedAmountCallback,
                              state_callback stateChangeCallback)
                              state_callback stateChangeCallback)
-    : Transport(lower, std::move(stateChangeCallback)), mPort(port), mPendingRecvCount(0),
+    : Transport(lower, std::move(stateChangeCallback)), mPort(port),
       mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
       mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
 	onRecv(recvCallback);
 	onRecv(recvCallback);
 
 
@@ -180,13 +181,24 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
 	// 1200 bytes.
 	// 1200 bytes.
 	// See https://tools.ietf.org/html/rfc8261#section-5
 	// See https://tools.ietf.org/html/rfc8261#section-5
 #if USE_PMTUD
 #if USE_PMTUD
-	// Enable SCTP path MTU discovery
-	spp.spp_flags |= SPP_PMTUD_ENABLE;
+	if (!mtu.has_value()) {
 #else
 #else
-	// Fall back to a safe MTU value.
-	spp.spp_flags |= SPP_PMTUD_DISABLE;
-	spp.spp_pathmtu = 1200;
+	if (false) {
 #endif
 #endif
+		// Enable SCTP path MTU discovery
+		spp.spp_flags |= SPP_PMTUD_ENABLE;
+		PLOG_VERBOSE << "Path MTU discovery enabled";
+
+	} else {
+		// Fall back to a safe MTU value.
+		spp.spp_flags |= SPP_PMTUD_DISABLE;
+		// The MTU value provided specifies the space available for chunks in the
+		// packet, so we also subtract the SCTP header size.
+		size_t pmtu = mtu.value_or(DEFAULT_MTU) - 12 - 37 - 8 - 40; // SCTP/DTLS/UDP/IPv6
+		spp.spp_pathmtu = uint32_t(pmtu);
+		PLOG_VERBOSE << "Path MTU discovery disabled, SCTP MTU set to " << pmtu;
+	}
+
 	if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &spp, sizeof(spp)))
 	if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &spp, sizeof(spp)))
 		throw std::runtime_error("Could not set socket option SCTP_PEER_ADDR_PARAMS, errno=" +
 		throw std::runtime_error("Could not set socket option SCTP_PEER_ADDR_PARAMS, errno=" +
 		                         std::to_string(errno));
 		                         std::to_string(errno));
@@ -249,7 +261,7 @@ bool SctpTransport::stop() {
 		return false;
 		return false;
 
 
 	mSendQueue.stop();
 	mSendQueue.stop();
-	safeFlush();
+	flush();
 	shutdown();
 	shutdown();
 	onRecv(nullptr);
 	onRecv(nullptr);
 	return true;
 	return true;
@@ -323,13 +335,20 @@ bool SctpTransport::send(message_ptr message) {
 	return false;
 	return false;
 }
 }
 
 
-void SctpTransport::closeStream(unsigned int stream) {
-	send(make_message(0, Message::Reset, uint16_t(stream)));
+bool SctpTransport::flush() {
+	try {
+		std::lock_guard lock(mSendMutex);
+		trySendQueue();
+		return true;
+
+	} catch (const std::exception &e) {
+		PLOG_WARNING << "SCTP flush: " << e.what();
+		return false;
+	}
 }
 }
 
 
-void SctpTransport::flush() {
-	std::lock_guard lock(mSendMutex);
-	trySendQueue();
+void SctpTransport::closeStream(unsigned int stream) {
+	send(make_message(0, Message::Reset, uint16_t(stream)));
 }
 }
 
 
 void SctpTransport::incoming(message_ptr message) {
 void SctpTransport::incoming(message_ptr message) {
@@ -349,6 +368,10 @@ void SctpTransport::incoming(message_ptr message) {
 	}
 	}
 
 
 	PLOG_VERBOSE << "Incoming size=" << message->size();
 	PLOG_VERBOSE << "Incoming size=" << message->size();
+
+	// TODO: There seems to be a possible data race between usrsctp_sendv() and usrsctp_conninput()
+	// As a mitigation, lock the send mutex before calling usrsctp_conninput()
+	std::lock_guard lock(mSendMutex);
 	usrsctp_conninput(this, message->data(), message->size(), 0);
 	usrsctp_conninput(this, message->data(), message->size(), 0);
 }
 }
 
 
@@ -413,6 +436,16 @@ void SctpTransport::doRecv() {
 	}
 	}
 }
 }
 
 
+void SctpTransport::doFlush() {
+	std::lock_guard lock(mSendMutex);
+	--mPendingFlushCount;
+	try {
+		trySendQueue();
+	} catch (const std::exception &e) {
+		PLOG_WARNING << e.what();
+	}
+}
+
 bool SctpTransport::trySendQueue() {
 bool SctpTransport::trySendQueue() {
 	// Requires mSendMutex to be locked
 	// Requires mSendMutex to be locked
 	while (auto next = mSendQueue.peek()) {
 	while (auto next = mSendQueue.peek()) {
@@ -519,13 +552,16 @@ void SctpTransport::updateBufferedAmount(uint16_t streamId, long delta) {
 	else
 	else
 		it->second = amount;
 		it->second = amount;
 
 
-	mSendMutex.unlock();
+	// Synchronously call the buffered amount callback
+	triggerBufferedAmount(streamId, amount);
+}
+
+void SctpTransport::triggerBufferedAmount(uint16_t streamId, size_t amount) {
 	try {
 	try {
 		mBufferedAmountCallback(streamId, amount);
 		mBufferedAmountCallback(streamId, amount);
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
-		PLOG_DEBUG << "SCTP buffered amount callback: " << e.what();
+		PLOG_WARNING << "SCTP buffered amount callback: " << e.what();
 	}
 	}
-	mSendMutex.lock();
 }
 }
 
 
 void SctpTransport::sendReset(uint16_t streamId) {
 void SctpTransport::sendReset(uint16_t streamId) {
@@ -555,17 +591,6 @@ void SctpTransport::sendReset(uint16_t streamId) {
 	}
 	}
 }
 }
 
 
-bool SctpTransport::safeFlush() {
-	try {
-		flush();
-		return true;
-
-	} catch (const std::exception &e) {
-		PLOG_WARNING << "SCTP flush: " << e.what();
-		return false;
-	}
-}
-
 void SctpTransport::handleUpcall() {
 void SctpTransport::handleUpcall() {
 	if (!mSock)
 	if (!mSock)
 		return;
 		return;
@@ -579,8 +604,10 @@ void SctpTransport::handleUpcall() {
 		mProcessor.enqueue(&SctpTransport::doRecv, this);
 		mProcessor.enqueue(&SctpTransport::doRecv, this);
 	}
 	}
 
 
-	if (events & SCTP_EVENT_WRITE)
-		mProcessor.enqueue(&SctpTransport::safeFlush, this);
+	if (events & SCTP_EVENT_WRITE && mPendingFlushCount == 0) {
+		++mPendingFlushCount;
+		mProcessor.enqueue(&SctpTransport::doFlush, this);
+	}
 }
 }
 
 
 int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
 int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
@@ -588,7 +615,6 @@ int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t
 		std::unique_lock lock(mWriteMutex);
 		std::unique_lock lock(mWriteMutex);
 		PLOG_VERBOSE << "Handle write, len=" << len;
 		PLOG_VERBOSE << "Handle write, len=" << len;
 
 
-		auto message = make_message(data, data + len);
 		if (!outgoing(make_message(data, data + len)))
 		if (!outgoing(make_message(data, data + len)))
 			return -1;
 			return -1;
 
 
@@ -696,7 +722,7 @@ void SctpTransport::processNotification(const union sctp_notification *notify, s
 		PLOG_VERBOSE << "SCTP dry event";
 		PLOG_VERBOSE << "SCTP dry event";
 		// It should not be necessary since the send callback should have been called already,
 		// It should not be necessary since the send callback should have been called already,
 		// but to be sure, let's try to send now.
 		// but to be sure, let's try to send now.
-		safeFlush();
+		flush();
 		break;
 		break;
 	}
 	}
 
 
@@ -759,7 +785,7 @@ size_t SctpTransport::bytesSent() { return mBytesSent; }
 
 
 size_t SctpTransport::bytesReceived() { return mBytesReceived; }
 size_t SctpTransport::bytesReceived() { return mBytesReceived; }
 
 
-std::optional<milliseconds> SctpTransport::rtt() {
+optional<milliseconds> SctpTransport::rtt() {
 	if (!mSock || state() != State::Connected)
 	if (!mSock || state() != State::Connected)
 		return nullopt;
 		return nullopt;
 
 
@@ -795,4 +821,4 @@ int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos,
 	return transport->handleWrite(static_cast<byte *>(data), len, tos, set_df);
 	return transport->handleWrite(static_cast<byte *>(data), len, tos, set_df);
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl

+ 17 - 14
src/sctptransport.hpp → src/impl/sctptransport.hpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2019-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,11 +16,10 @@
  * 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_SCTP_TRANSPORT_H
-#define RTC_SCTP_TRANSPORT_H
+#ifndef RTC_IMPL_SCTP_TRANSPORT_H
+#define RTC_IMPL_SCTP_TRANSPORT_H
 
 
-#include "include.hpp"
-#include "peerconnection.hpp"
+#include "common.hpp"
 #include "processor.hpp"
 #include "processor.hpp"
 #include "queue.hpp"
 #include "queue.hpp"
 #include "transport.hpp"
 #include "transport.hpp"
@@ -34,7 +33,7 @@
 
 
 #include "usrsctp.h"
 #include "usrsctp.h"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 class SctpTransport final : public Transport {
 class SctpTransport final : public Transport {
 public:
 public:
@@ -43,21 +42,22 @@ public:
 
 
 	using amount_callback = std::function<void(uint16_t streamId, size_t amount)>;
 	using amount_callback = std::function<void(uint16_t streamId, size_t amount)>;
 
 
-	SctpTransport(std::shared_ptr<Transport> lower, uint16_t port, message_callback recvCallback,
-	              amount_callback bufferedAmountCallback, state_callback stateChangeCallback);
+	SctpTransport(shared_ptr<Transport> lower, uint16_t port, optional<size_t> mtu,
+	              message_callback recvCallback, amount_callback bufferedAmountCallback,
+	              state_callback stateChangeCallback);
 	~SctpTransport();
 	~SctpTransport();
 
 
 	void start() override;
 	void start() override;
 	bool stop() override;
 	bool stop() override;
 	bool send(message_ptr message) override; // false if buffered
 	bool send(message_ptr message) override; // false if buffered
+	bool flush();
 	void closeStream(unsigned int stream);
 	void closeStream(unsigned int stream);
-	void flush();
 
 
 	// Stats
 	// Stats
 	void clearStats();
 	void clearStats();
 	size_t bytesSent();
 	size_t bytesSent();
 	size_t bytesReceived();
 	size_t bytesReceived();
-	std::optional<std::chrono::milliseconds> rtt();
+	optional<std::chrono::milliseconds> rtt();
 
 
 private:
 private:
 	// Order seems wrong but these are the actual values
 	// Order seems wrong but these are the actual values
@@ -79,11 +79,12 @@ private:
 	bool outgoing(message_ptr message) override;
 	bool outgoing(message_ptr message) override;
 
 
 	void doRecv();
 	void doRecv();
+	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, long delta);
+	void triggerBufferedAmount(uint16_t streamId, size_t amount);
 	void sendReset(uint16_t streamId);
 	void sendReset(uint16_t streamId);
-	bool safeFlush();
 
 
 	void handleUpcall();
 	void handleUpcall();
 	int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
 	int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
@@ -95,8 +96,10 @@ private:
 	struct socket *mSock;
 	struct socket *mSock;
 
 
 	Processor mProcessor;
 	Processor mProcessor;
-	std::atomic<int> mPendingRecvCount;
-	std::mutex mRecvMutex, mSendMutex;
+	std::atomic<int> mPendingRecvCount = 0;
+	std::atomic<int> mPendingFlushCount = 0;
+	std::mutex mRecvMutex;
+	std::recursive_mutex mSendMutex; // buffered amount callback is synchronous
 	Queue<message_ptr> mSendQueue;
 	Queue<message_ptr> mSendQueue;
 	std::map<uint16_t, size_t> mBufferedAmount;
 	std::map<uint16_t, size_t> mBufferedAmount;
 	amount_callback mBufferedAmountCallback;
 	amount_callback mBufferedAmountCallback;
@@ -119,6 +122,6 @@ private:
 	static std::shared_mutex InstancesMutex;
 	static std::shared_mutex InstancesMutex;
 };
 };
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 5 - 4
src/tcptransport.cpp → src/impl/tcptransport.cpp

@@ -17,6 +17,7 @@
  */
  */
 
 
 #include "tcptransport.hpp"
 #include "tcptransport.hpp"
+#include "globals.hpp"
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
@@ -27,7 +28,7 @@
 #include <unistd.h>
 #include <unistd.h>
 #endif
 #endif
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 using std::to_string;
 using std::to_string;
 
 
@@ -63,7 +64,7 @@ int SelectInterrupter::prepare(fd_set &readfds, [[maybe_unused]] fd_set &writefd
 	return SOCKET_TO_INT(mDummySock) + 1;
 	return SOCKET_TO_INT(mDummySock) + 1;
 #else
 #else
 	char dummy;
 	char dummy;
-	::read(mPipeIn, &dummy, 1);
+	(void)::read(mPipeIn, &dummy, 1);
 	FD_SET(mPipeIn, &readfds);
 	FD_SET(mPipeIn, &readfds);
 	return mPipeIn + 1;
 	return mPipeIn + 1;
 #endif
 #endif
@@ -78,7 +79,7 @@ void SelectInterrupter::interrupt() {
 	}
 	}
 #else
 #else
 	char dummy = 0;
 	char dummy = 0;
-	::write(mPipeOut, &dummy, 1);
+	(void)::write(mPipeOut, &dummy, 1);
 #endif
 #endif
 }
 }
 
 
@@ -398,6 +399,6 @@ int TcpTransport::prepareSelect(fd_set &readfds, fd_set &writefds) {
 
 
 void TcpTransport::interruptSelect() { mInterrupter.interrupt(); }
 void TcpTransport::interruptSelect() { mInterrupter.interrupt(); }
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 5 - 5
src/tcptransport.hpp → src/impl/tcptransport.hpp

@@ -16,10 +16,10 @@
  * 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_TCP_TRANSPORT_H
-#define RTC_TCP_TRANSPORT_H
+#ifndef RTC_IMPL_TCP_TRANSPORT_H
+#define RTC_IMPL_TCP_TRANSPORT_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "queue.hpp"
 #include "queue.hpp"
 #include "transport.hpp"
 #include "transport.hpp"
 
 
@@ -31,7 +31,7 @@
 // Use the socket defines from libjuice
 // Use the socket defines from libjuice
 #include "../deps/libjuice/src/socket.h"
 #include "../deps/libjuice/src/socket.h"
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 // Utility class to interrupt select()
 // Utility class to interrupt select()
 class SelectInterrupter {
 class SelectInterrupter {
@@ -85,7 +85,7 @@ private:
 	Queue<message_ptr> mSendQueue;
 	Queue<message_ptr> mSendQueue;
 };
 };
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif
 
 

+ 30 - 25
src/threadpool.cpp → src/impl/threadpool.cpp

@@ -21,21 +21,19 @@
 #include <cstdlib>
 #include <cstdlib>
 
 
 namespace {
 namespace {
-	void joinThreadPoolInstance() {
-		rtc::ThreadPool::Instance().join();
-	}
-}
 
 
-namespace rtc {
+void joinThreadPoolInstance() { rtc::impl::ThreadPool::Instance().join(); }
+
+} // namespace
+
+namespace rtc::impl {
 
 
 ThreadPool &ThreadPool::Instance() {
 ThreadPool &ThreadPool::Instance() {
 	static ThreadPool *instance = new ThreadPool;
 	static ThreadPool *instance = new ThreadPool;
 	return *instance;
 	return *instance;
 }
 }
 
 
-ThreadPool::ThreadPool() {
-	std::atexit(joinThreadPoolInstance);
-}
+ThreadPool::ThreadPool() { std::atexit(joinThreadPoolInstance); }
 
 
 ThreadPool::~ThreadPool() {}
 ThreadPool::~ThreadPool() {}
 
 
@@ -46,23 +44,30 @@ int ThreadPool::count() const {
 
 
 void ThreadPool::spawn(int count) {
 void ThreadPool::spawn(int count) {
 	std::unique_lock lock(mWorkersMutex);
 	std::unique_lock lock(mWorkersMutex);
-	mJoining = false;
 	while (count-- > 0)
 	while (count-- > 0)
 		mWorkers.emplace_back(std::bind(&ThreadPool::run, this));
 		mWorkers.emplace_back(std::bind(&ThreadPool::run, this));
 }
 }
 
 
 void ThreadPool::join() {
 void ThreadPool::join() {
-	std::unique_lock lock(mWorkersMutex);
-	mJoining = true;
-	mCondition.notify_all();
+	{
+		std::unique_lock lock(mMutex);
+		mWaitingCondition.wait(lock, [&]() { return mBusyWorkers == 0; });
+		mJoining = true;
+		mTasksCondition.notify_all();
+	}
 
 
+	std::unique_lock lock(mWorkersMutex);
 	for (auto &w : mWorkers)
 	for (auto &w : mWorkers)
 		w.join();
 		w.join();
 
 
 	mWorkers.clear();
 	mWorkers.clear();
+
+	mJoining = false;
 }
 }
 
 
 void ThreadPool::run() {
 void ThreadPool::run() {
+	++mBusyWorkers;
+	scope_guard guard([&]() { --mBusyWorkers; });
 	while (runOne()) {
 	while (runOne()) {
 	}
 	}
 }
 }
@@ -77,26 +82,26 @@ bool ThreadPool::runOne() {
 
 
 std::function<void()> ThreadPool::dequeue() {
 std::function<void()> ThreadPool::dequeue() {
 	std::unique_lock lock(mMutex);
 	std::unique_lock lock(mMutex);
-	while (true) {
+	while (!mJoining) {
+		std::optional<clock::time_point> time;
 		if (!mTasks.empty()) {
 		if (!mTasks.empty()) {
-			if (mTasks.top().time <= clock::now()) {
+			time = mTasks.top().time;
+			if (*time <= clock::now()) {
 				auto func = std::move(mTasks.top().func);
 				auto func = std::move(mTasks.top().func);
 				mTasks.pop();
 				mTasks.pop();
 				return func;
 				return func;
 			}
 			}
-
-			if (mJoining)
-				break;
-
-			mCondition.wait_until(lock, mTasks.top().time);
-		} else {
-			if (mJoining)
-				break;
-
-			mCondition.wait(lock);
 		}
 		}
+
+		--mBusyWorkers;
+		scope_guard guard([&]() { ++mBusyWorkers; });
+		mWaitingCondition.notify_all();
+		if(time)
+			mTasksCondition.wait_until(lock, *time);
+		else
+			mTasksCondition.wait(lock);
 	}
 	}
 	return nullptr;
 	return nullptr;
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl

+ 8 - 7
src/threadpool.hpp → src/impl/threadpool.hpp

@@ -16,10 +16,10 @@
  * 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_THREADPOOL_H
-#define RTC_THREADPOOL_H
+#ifndef RTC_IMPL_THREADPOOL_H
+#define RTC_IMPL_THREADPOOL_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 #include "init.hpp"
 #include "init.hpp"
 
 
 #include <chrono>
 #include <chrono>
@@ -34,7 +34,7 @@
 #include <thread>
 #include <thread>
 #include <vector>
 #include <vector>
 
 
-namespace rtc {
+namespace rtc::impl {
 
 
 template <class F, class... Args>
 template <class F, class... Args>
 using invoke_future_t = std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>;
 using invoke_future_t = std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>;
@@ -72,6 +72,7 @@ protected:
 	std::function<void()> dequeue(); // returns null function if joining
 	std::function<void()> dequeue(); // returns null function if joining
 
 
 	std::vector<std::thread> mWorkers;
 	std::vector<std::thread> mWorkers;
+	std::atomic<int> mBusyWorkers = 0;
 	std::atomic<bool> mJoining = false;
 	std::atomic<bool> mJoining = false;
 
 
 	struct Task {
 	struct Task {
@@ -82,8 +83,8 @@ protected:
 	};
 	};
 	std::priority_queue<Task, std::deque<Task>, std::greater<Task>> mTasks;
 	std::priority_queue<Task, std::deque<Task>, std::greater<Task>> mTasks;
 
 
+	std::condition_variable mTasksCondition, mWaitingCondition;
 	mutable std::mutex mMutex, mWorkersMutex;
 	mutable std::mutex mMutex, mWorkersMutex;
-	std::condition_variable mCondition;
 };
 };
 
 
 template <class F, class... Args>
 template <class F, class... Args>
@@ -114,10 +115,10 @@ auto ThreadPool::schedule(clock::time_point time, F &&f, Args &&...args)
 	std::future<R> result = task->get_future();
 	std::future<R> result = task->get_future();
 
 
 	mTasks.push({time, [task = std::move(task), token = Init::Token()]() { return (*task)(); }});
 	mTasks.push({time, [task = std::move(task), token = Init::Token()]() { return (*task)(); }});
-	mCondition.notify_one();
+	mTasksCondition.notify_one();
 	return result;
 	return result;
 }
 }
 
 
-} // namespace rtc
+} // namespace rtc::impl
 
 
 #endif
 #endif

+ 0 - 0
src/tls.cpp → src/impl/tls.cpp


+ 1 - 1
src/tls.hpp → src/impl/tls.hpp

@@ -19,7 +19,7 @@
 #ifndef RTC_TLS_H
 #ifndef RTC_TLS_H
 #define RTC_TLS_H
 #define RTC_TLS_H
 
 
-#include "include.hpp"
+#include "common.hpp"
 
 
 #if USE_GNUTLS
 #if USE_GNUTLS
 
 

Some files were not shown because too many files changed in this diff