Browse Source

Merge pull request #1 from paullouisageneau/master

update from original
Gasper 1 year ago
parent
commit
d684b3c608
100 changed files with 3050 additions and 2068 deletions
  1. 1 1
      .github/workflows/build-nomedia.yml
  2. 1 1
      .github/workflows/build-openssl.yml
  3. 24 0
      .github/workflows/check-version.yml
  4. 3 4
      BUILDING.md
  5. 47 32
      CMakeLists.txt
  6. 30 2
      DOC.md
  7. 0 1
      Jamfile
  8. 11 3
      Makefile
  9. 5 4
      README.md
  10. 9 0
      cmake/version.h.in
  11. 1 1
      deps/json
  12. 1 1
      deps/libjuice
  13. 1 1
      deps/plog
  14. 1 1
      deps/usrsctp
  15. 0 339
      examples/client-benchmark/LICENSE
  16. 0 339
      examples/client/LICENSE
  17. 2 0
      examples/client/parse_cl.cpp
  18. 1 1
      examples/copy-paste-capi/answerer.c
  19. 1 1
      examples/copy-paste-capi/offerer.c
  20. 3 4
      examples/media-sfu/main.cpp
  21. 219 169
      examples/signaling-server-rust/Cargo.lock
  22. 3 2
      examples/streamer/fileparser.cpp
  23. 1 1
      examples/streamer/helpers.cpp
  24. 8 12
      examples/streamer/main.cpp
  25. 1 1
      examples/streamer/stream.hpp
  26. 56 0
      include/rtc/av1rtppacketizer.hpp
  27. 5 5
      include/rtc/candidate.hpp
  28. 29 0
      include/rtc/configuration.hpp
  29. 45 24
      include/rtc/description.hpp
  30. 23 0
      include/rtc/frameinfo.hpp
  31. 3 3
      include/rtc/global.hpp
  32. 0 32
      include/rtc/h264packetizationhandler.hpp
  33. 43 0
      include/rtc/h264rtpdepacketizer.hpp
  34. 22 25
      include/rtc/h264rtppacketizer.hpp
  35. 186 0
      include/rtc/h265nalunit.hpp
  36. 56 0
      include/rtc/h265rtppacketizer.hpp
  37. 0 48
      include/rtc/mediachainablehandler.hpp
  38. 35 17
      include/rtc/mediahandler.hpp
  39. 0 112
      include/rtc/mediahandlerelement.hpp
  40. 0 36
      include/rtc/mediahandlerrootelement.hpp
  41. 10 2
      include/rtc/message.hpp
  42. 73 4
      include/rtc/nalunit.hpp
  43. 0 32
      include/rtc/opuspacketizationhandler.hpp
  44. 0 50
      include/rtc/opusrtppacketizer.hpp
  45. 20 7
      include/rtc/peerconnection.hpp
  46. 36 0
      include/rtc/plihandler.hpp
  47. 18 3
      include/rtc/reliability.hpp
  48. 111 34
      include/rtc/rtc.h
  49. 9 6
      include/rtc/rtc.hpp
  50. 17 27
      include/rtc/rtcpnackresponder.hpp
  51. 15 10
      include/rtc/rtcpreceivingsession.hpp
  52. 14 15
      include/rtc/rtcpsrreporter.hpp
  53. 22 20
      include/rtc/rtp.hpp
  54. 34 0
      include/rtc/rtpdepacketizer.hpp
  55. 13 5
      include/rtc/rtppacketizationconfig.hpp
  56. 54 11
      include/rtc/rtppacketizer.hpp
  57. 4 0
      include/rtc/track.hpp
  58. 9 0
      include/rtc/version.h
  59. 4 9
      include/rtc/websocket.hpp
  60. 2 9
      include/rtc/websocketserver.hpp
  61. 3 4
      pages/Makefile
  62. 0 1
      pages/content/extra/CNAME
  63. 1 1
      pages/content/pages/index.md
  64. 30 2
      pages/content/pages/reference.md
  65. 7 2
      pages/pelicanconf.py
  66. 225 0
      src/av1rtppacketizer.cpp
  67. 15 15
      src/candidate.cpp
  68. 207 97
      src/capi.cpp
  69. 2 4
      src/channel.cpp
  70. 1 1
      src/configuration.cpp
  71. 1 7
      src/datachannel.cpp
  72. 238 69
      src/description.cpp
  73. 14 23
      src/global.cpp
  74. 0 20
      src/h264packetizationhandler.cpp
  75. 144 0
      src/h264rtpdepacketizer.cpp
  76. 21 76
      src/h264rtppacketizer.cpp
  77. 100 0
      src/h265nalunit.cpp
  78. 113 0
      src/h265rtppacketizer.cpp
  79. 112 36
      src/impl/certificate.cpp
  80. 6 5
      src/impl/certificate.hpp
  81. 2 2
      src/impl/channel.hpp
  82. 68 33
      src/impl/datachannel.cpp
  83. 27 9
      src/impl/dtlssrtptransport.cpp
  84. 10 8
      src/impl/dtlssrtptransport.hpp
  85. 66 41
      src/impl/dtlstransport.cpp
  86. 3 0
      src/impl/dtlstransport.hpp
  87. 3 2
      src/impl/httpproxytransport.cpp
  88. 8 7
      src/impl/icetransport.cpp
  89. 1 1
      src/impl/init.cpp
  90. 3 1
      src/impl/internals.hpp
  91. 142 69
      src/impl/peerconnection.cpp
  92. 7 2
      src/impl/peerconnection.hpp
  93. 4 3
      src/impl/pollservice.cpp
  94. 64 36
      src/impl/sctptransport.cpp
  95. 1 0
      src/impl/sctptransport.hpp
  96. 2 2
      src/impl/tcpserver.cpp
  97. 2 2
      src/impl/tcpserver.hpp
  98. 27 3
      src/impl/tcptransport.cpp
  99. 26 15
      src/impl/tls.cpp
  100. 2 2
      src/impl/tls.hpp

+ 1 - 1
.github/workflows/build-nomedia.yml

@@ -25,7 +25,7 @@ jobs:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
     - uses: ilammy/msvc-dev-cmd@v1
     - uses: ilammy/msvc-dev-cmd@v1
     - name: install packages
     - name: install packages
-      run: choco install openssl
+      run: choco install openssl --version=3.1.1
     - name: submodules
     - name: submodules
       run: git submodule update --init --recursive --depth 1
       run: git submodule update --init --recursive --depth 1
     - name: cmake
     - name: cmake

+ 1 - 1
.github/workflows/build-openssl.yml

@@ -41,7 +41,7 @@ jobs:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
     - uses: ilammy/msvc-dev-cmd@v1
     - uses: ilammy/msvc-dev-cmd@v1
     - name: install packages
     - name: install packages
-      run: choco install openssl
+      run: choco install openssl --version=3.1.1
     - name: submodules
     - name: submodules
       run: git submodule update --init --recursive --depth 1
       run: git submodule update --init --recursive --depth 1
     - name: cmake
     - name: cmake

+ 24 - 0
.github/workflows/check-version.yml

@@ -0,0 +1,24 @@
+name: Check version.h values
+on:
+  push:
+    branches:
+    - master
+  pull_request:
+jobs:
+  check-version:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: install packages
+      run: sudo apt update && sudo apt install libssl-dev libsrtp2-dev
+    - name: submodules
+      run: git submodule update --init --recursive --depth 1
+    - name: cmake
+      run: cmake -B build -DUSE_GNUTLS=0 -DUSE_SYSTEM_SRTP=1 -DWARNINGS_AS_ERRORS=1
+    - name: check diff
+      run: |
+        if ! git diff --exit-code
+        then
+          exit 1
+        fi
+

+ 3 - 4
BUILDING.md

@@ -19,10 +19,9 @@ $ git submodule update --init --recursive --depth 1
 
 
 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 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.
+Options `USE_GNUTLS` and `USE_MBEDTLS` allow to switch the cryptographic backend to GnuTLS and Mbed TLS respectively, otherwise OpenSSL is selected by default. The option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice as system library.
 
 
-The option `PREFER_SYSTEM_LIB` allow to link against the system library rather than building all the submodule.
-Options `USE_SYSTEM_SRTP`, `USE_SYSTEM_JUICE`, `USE_SYSTEM_USRSCTP`, `USE_SYSTEM_PLOG` and `USE_SYSTEM_JSON` allow to do the same but per submodule, for libsrtp, libjuice, libusrsctp, Plog and Nlohmann JSON respectively.
+The option `PREFER_SYSTEM_LIB` allows to link against the system library rather than building all the submodule. Options `USE_SYSTEM_SRTP`, `USE_SYSTEM_JUICE`, `USE_SYSTEM_USRSCTP`, `USE_SYSTEM_PLOG` and `USE_SYSTEM_JSON` allow to do the same but per submodule, for libsrtp, libjuice, libusrsctp, Plog and Nlohmann JSON respectively.
 
 
 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.
 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.
 
 
@@ -78,7 +77,7 @@ $ nmake
 
 
 ## Build directly with Make (Linux only)
 ## 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.
+Options `USE_GNUTLS` and `USE_MBEDTLS` allow to switch the cryptographic backend to GnuTLS and Mbed TLS respectively, otherwise OpenSSL is selected by default. The option `USE_NICE` allows to switch between libjuice as submodule (default) and libnice as system library.
 
 
 If you only need Data Channels, the option `NO_MEDIA` removes media support. Similarly, `NO_WEBSOCKET` removes WebSocket support.
 If you only need Data Channels, the option `NO_MEDIA` removes media support. Similarly, `NO_WEBSOCKET` removes WebSocket support.
 
 

+ 47 - 32
CMakeLists.txt

@@ -1,12 +1,14 @@
 cmake_minimum_required(VERSION 3.7)
 cmake_minimum_required(VERSION 3.7)
 project(libdatachannel
 project(libdatachannel
-	VERSION 0.18.4
+	VERSION 0.20.1
 	LANGUAGES CXX)
 	LANGUAGES CXX)
 set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets")
 set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets")
 
 
+include(GNUInstallDirs)
+
 # Options
 # Options
-option(USE_MBEDTLS "Use Mbed TLS instead of OpenSSL" OFF)
 option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
 option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
+option(USE_MBEDTLS "Use Mbed TLS instead of OpenSSL" OFF)
 option(USE_NICE "Use libnice instead of libjuice" OFF)
 option(USE_NICE "Use libnice instead of libjuice" OFF)
 option(PREFER_SYSTEM_LIB "Prefer system libraries over deps folder" OFF)
 option(PREFER_SYSTEM_LIB "Prefer system libraries over deps folder" OFF)
 option(USE_SYSTEM_SRTP "Use system libSRTP" ${PREFER_SYSTEM_LIB})
 option(USE_SYSTEM_SRTP "Use system libSRTP" ${PREFER_SYSTEM_LIB})
@@ -22,11 +24,10 @@ option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
 option(CAPI_STDCALL "Set calling convention of C API callbacks stdcall" OFF)
 option(CAPI_STDCALL "Set calling convention of C API callbacks stdcall" OFF)
 option(SCTP_DEBUG "Enable SCTP debugging output to verbose log" OFF)
 option(SCTP_DEBUG "Enable SCTP debugging output to verbose log" OFF)
 
 
-if (USE_MBEDTLS AND USE_GNUTLS)
-	message(FATAL_ERROR "Both USE_MBEDTLS and USE_GNUTLS can not be enabled at the same time")
+if (USE_GNUTLS AND USE_MBEDTLS)
+	message(FATAL_ERROR "Both USE_MBEDTLS and USE_GNUTLS cannot be enabled at the same time")
 endif()
 endif()
 
 
-
 if(USE_GNUTLS)
 if(USE_GNUTLS)
 	option(USE_NETTLE "Use Nettle in libjuice" ON)
 	option(USE_NETTLE "Use Nettle in libjuice" ON)
 else()
 else()
@@ -54,7 +55,6 @@ if(WIN32)
 	if(MSVC)
 	if(MSVC)
 		add_definitions(-DNOMINMAX)
 		add_definitions(-DNOMINMAX)
 		add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 		add_definitions(-D_CRT_SECURE_NO_WARNINGS)
-		add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
 	endif()
 	endif()
 endif()
 endif()
 
 
@@ -64,6 +64,7 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/global.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
@@ -74,17 +75,18 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.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/opuspacketizationhandler.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtpdepacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtpdepacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h265rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/av1rtppacketizer.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/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/rtcpnackresponder.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtp.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtp.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/plihandler.cpp
 )
 )
 
 
 set(LIBDATACHANNEL_HEADERS
 set(LIBDATACHANNEL_HEADERS
@@ -98,6 +100,7 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/global.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/global.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/frameinfo.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
@@ -109,16 +112,18 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.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/opuspacketizationhandler.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtpdepacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtpdepacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265rtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/av1rtppacketizer.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/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
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpnackresponder.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/utils.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/utils.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/plihandler.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/version.h
 )
 )
 
 
 set(LIBDATACHANNEL_IMPL_SOURCES
 set(LIBDATACHANNEL_IMPL_SOURCES
@@ -191,6 +196,7 @@ set(TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/negotiated.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/negotiated.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/test/reliability.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/turn_connectivity.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/turn_connectivity.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/track.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/track.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/capi_connectivity.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test/capi_connectivity.cpp
@@ -243,17 +249,20 @@ else()
 	option(sctp_build_programs OFF)
 	option(sctp_build_programs OFF)
 	option(sctp_inet OFF)
 	option(sctp_inet OFF)
 	option(sctp_inet6 OFF)
 	option(sctp_inet6 OFF)
+	option(sctp_werror OFF)
 	set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
 	set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
 	add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
 	add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
 	if (MSYS OR MINGW)
 	if (MSYS OR MINGW)
 		target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
 		target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
 	endif()
 	endif()
-	if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-    	target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
-	endif()
 	add_library(Usrsctp::Usrsctp ALIAS usrsctp)
 	add_library(Usrsctp::Usrsctp ALIAS usrsctp)
 endif()
 endif()
 
 
+configure_file (
+    ${PROJECT_SOURCE_DIR}/cmake/version.h.in
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/version.h
+)
+
 add_library(datachannel SHARED
 add_library(datachannel SHARED
 	${LIBDATACHANNEL_SOURCES}
 	${LIBDATACHANNEL_SOURCES}
 	${LIBDATACHANNEL_HEADERS}
 	${LIBDATACHANNEL_HEADERS}
@@ -261,9 +270,16 @@ add_library(datachannel SHARED
 	${LIBDATACHANNEL_IMPL_HEADERS})
 	${LIBDATACHANNEL_IMPL_HEADERS})
 set_target_properties(datachannel PROPERTIES
 set_target_properties(datachannel PROPERTIES
 	VERSION ${PROJECT_VERSION}
 	VERSION ${PROJECT_VERSION}
-	SOVERSION ${PROJECT_VERSION}
+	SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
 	CXX_STANDARD 17
 	CXX_STANDARD 17
 	CXX_VISIBILITY_PRESET default)
 	CXX_VISIBILITY_PRESET default)
+
+if(APPLE)
+	set_target_properties(datachannel PROPERTIES
+		VERSION ${PROJECT_VERSION_MAJOR}
+		SOVERSION ${PROJECT_VERSION_MAJOR})
+endif()
+
 target_compile_definitions(datachannel PRIVATE RTC_EXPORTS)
 target_compile_definitions(datachannel PRIVATE RTC_EXPORTS)
 
 
 add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
 add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
@@ -356,9 +372,8 @@ if (USE_GNUTLS)
 	endif()
 	endif()
 elseif(USE_MBEDTLS)
 elseif(USE_MBEDTLS)
 	find_package(MbedTLS 3 REQUIRED)
 	find_package(MbedTLS 3 REQUIRED)
-
-	target_compile_definitions(datachannel PRIVATE USE_MBEDTLS)
-	target_compile_definitions(datachannel-static PRIVATE USE_MBEDTLS)
+	target_compile_definitions(datachannel PRIVATE USE_MBEDTLS=1)
+	target_compile_definitions(datachannel-static PRIVATE USE_MBEDTLS=1)
 	target_link_libraries(datachannel PRIVATE MbedTLS::MbedTLS)
 	target_link_libraries(datachannel PRIVATE MbedTLS::MbedTLS)
 	target_link_libraries(datachannel-static PRIVATE MbedTLS::MbedTLS)
 	target_link_libraries(datachannel-static PRIVATE MbedTLS::MbedTLS)
 else()
 else()
@@ -436,13 +451,13 @@ if(WARNINGS_AS_ERRORS)
 endif()
 endif()
 
 
 install(TARGETS datachannel EXPORT LibDataChannelTargets
 install(TARGETS datachannel EXPORT LibDataChannelTargets
-	RUNTIME DESTINATION bin
-	LIBRARY DESTINATION lib
-	ARCHIVE DESTINATION lib
+	RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+	LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
 )
 
 
 install(FILES ${LIBDATACHANNEL_HEADERS}
 install(FILES ${LIBDATACHANNEL_HEADERS}
-	DESTINATION include/rtc
+	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rtc
 )
 )
 
 
 # Export targets
 # Export targets
@@ -450,13 +465,13 @@ install(
 	EXPORT LibDataChannelTargets
 	EXPORT LibDataChannelTargets
 	FILE LibDataChannelTargets.cmake
 	FILE LibDataChannelTargets.cmake
 	NAMESPACE LibDataChannel::
 	NAMESPACE LibDataChannel::
-	DESTINATION lib/cmake/LibDataChannel
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LibDataChannel
 )
 )
 
 
 # Export config
 # Export config
 install(
 install(
 	FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibDataChannelConfig.cmake
 	FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibDataChannelConfig.cmake
-	DESTINATION lib/cmake/LibDataChannel
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LibDataChannel
 )
 )
 
 
 # Export config version
 # Export config version
@@ -466,7 +481,7 @@ write_basic_package_version_file(
 	VERSION ${PROJECT_VERSION}
 	VERSION ${PROJECT_VERSION}
 	COMPATIBILITY SameMajorVersion)
 	COMPATIBILITY SameMajorVersion)
 install(FILES ${CMAKE_BINARY_DIR}/LibDataChannelConfigVersion.cmake
 install(FILES ${CMAKE_BINARY_DIR}/LibDataChannelConfigVersion.cmake
-	DESTINATION lib/cmake/LibDataChannel)
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LibDataChannel)
 
 
 # Tests
 # Tests
 if(NOT NO_TESTS)
 if(NOT NO_TESTS)

+ 30 - 2
DOC.md

@@ -383,6 +383,20 @@ Arguments:
 
 
 Return value: the maximum stream ID (`stream` for a Data Channel may be set from 0 to this value included) or a negative error code
 Return value: the maximum stream ID (`stream` for a Data Channel may be set from 0 to this value included) or a negative error code
 
 
+#### rtcGetRemoteMaxMessageSize
+
+```
+int rtcGetRemoteMaxMessageSize(int pc)
+```
+
+Retrieves the maximum message size for data channels on the peer connection as negotiated with the remote peer.
+
+Arguments:
+
+- `pc`: the Peer Connection identifier
+
+Return value: the maximum message size for data channels or a negative error code
+
 ### Channel (Common API for Data Channel, Track, and WebSocket)
 ### Channel (Common API for 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.
 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.
@@ -519,6 +533,20 @@ Arguments:
 
 
 Return value: `true` if the channel exists and is closed (not open and not connecting), `false` otherwise
 Return value: `true` if the channel exists and is closed (not open and not connecting), `false` otherwise
 
 
+#### rtcGetMaxMessageSize
+
+```
+int rtcGetMaxMessageSize(int id)
+```
+
+Retrieves the maximum message size for the channel.
+
+Arguments:
+
+- `id`: the channel identifier
+
+Return value: the maximum message size or a negative error code
+
 #### rtcGetBufferedAmount
 #### rtcGetBufferedAmount
 
 
 ```
 ```
@@ -616,8 +644,8 @@ Arguments:
   - `reliability`: a structure of reliability settings containing:
   - `reliability`: a structure of reliability settings containing:
     - `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
     - `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
     - `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
     - `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
-    - `maxPacketLifeTime`: if unreliable, maximum packet life time in milliseconds
-    - `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of retransmissions (0 means no retransmission)
+    - `maxPacketLifeTime`: if unreliable, time window in milliseconds during which transmissions and retransmissions may occur
+    - `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of attempted retransmissions (0 means no retransmission)
   - `protocol` (optional): a user-defined UTF-8 string representing the Data Channel protocol, empty if NULL
   - `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
   - `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
   - `manualStream`: if `true`, the Data Channel will use `stream` as stream ID, else an available id is automatically selected

+ 0 - 1
Jamfile

@@ -26,7 +26,6 @@ lib libdatachannel
 	<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
 	<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
 	<toolset>msvc:<define>NOMINMAX
 	<toolset>msvc:<define>NOMINMAX
 	<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
 	<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
-	<toolset>msvc:<define>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
 	<library>/libdatachannel//usrsctp
 	<library>/libdatachannel//usrsctp
 	<library>/libdatachannel//juice
 	<library>/libdatachannel//juice
 	<library>/libdatachannel//plog
 	<library>/libdatachannel//plog

+ 11 - 3
Makefile

@@ -11,6 +11,7 @@ LIBS=
 LOCALLIBS=libusrsctp.a
 LOCALLIBS=libusrsctp.a
 USRSCTP_DIR=deps/usrsctp
 USRSCTP_DIR=deps/usrsctp
 SRTP_DIR=deps/libsrtp
 SRTP_DIR=deps/libsrtp
+SRTP_CONFIGURE_FLAGS=
 JUICE_DIR=deps/libjuice
 JUICE_DIR=deps/libjuice
 PLOG_DIR=deps/plog
 PLOG_DIR=deps/plog
 
 
@@ -18,12 +19,19 @@ INCLUDES=-Isrc -Iinclude/rtc -Iinclude -I$(PLOG_DIR)/include -I$(USRSCTP_DIR)/us
 LDLIBS=
 LDLIBS=
 
 
 USE_GNUTLS ?= 0
 USE_GNUTLS ?= 0
+USE_MBEDTLS ?= 0
 ifneq ($(USE_GNUTLS), 0)
 ifneq ($(USE_GNUTLS), 0)
-        CPPFLAGS+=-DUSE_GNUTLS=1
+ifneq ($(USE_MBEDTLS), 0)
+$(error Both USE_MBEDTLS and USE_GNUTLS cannot be enabled at the same time)
+endif
+		CPPFLAGS+=-DUSE_GNUTLS=1
         LIBS+=gnutls
         LIBS+=gnutls
+else ifneq ($(USE_MBEDTLS), 0)
+        CPPFLAGS+=-DUSE_MBEDTLS=1
+        LIBS+=mbedtls
 else
 else
-        CPPFLAGS+=-DUSE_GNUTLS=0
         LIBS+=openssl
         LIBS+=openssl
+        SRTP_CONFIGURE_FLAGS+=--enable-openssl
 endif
 endif
 
 
 USE_NICE ?= 0
 USE_NICE ?= 0
@@ -122,7 +130,7 @@ libusrsctp.a:
 
 
 libsrtp2.a:
 libsrtp2.a:
 	cd $(SRTP_DIR) && \
 	cd $(SRTP_DIR) && \
-		./configure && \
+		./configure $(SRTP_CONFIGURE_FLAGS) && \
 		make
 		make
 	cp $(SRTP_DIR)/libsrtp2.a .
 	cp $(SRTP_DIR)/libsrtp2.a .
 
 

+ 5 - 4
README.md

@@ -5,7 +5,8 @@
 [![Build with Mbed TLS](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-mbedtls.yml/badge.svg)](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-mbedtls.yml)
 [![Build with Mbed TLS](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-mbedtls.yml/badge.svg)](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-mbedtls.yml)
 [![Build with OpenSSL](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-openssl.yml/badge.svg)](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-openssl.yml)
 [![Build with OpenSSL](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-openssl.yml/badge.svg)](https://github.com/paullouisageneau/libdatachannel/actions/workflows/build-openssl.yml)
 
 
-[![AUR package](https://repology.org/badge/version-for-repo/aur/libdatachannel.svg)](https://repology.org/project/libdatachannel/versions) [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/libdatachannel.svg)](https://repology.org/project/libdatachannel/versions) [![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/libdatachannel.svg)](https://repology.org/project/libdatachannel/versions)
+[![Packaging status](https://repology.org/badge/tiny-repos/libdatachannel.svg)](https://repology.org/project/libdatachannel/versions)
+[![Latest packaged version](https://repology.org/badge/latest-versions/libdatachannel.svg)](https://repology.org/project/libdatachannel/versions)
 [![Gitter](https://badges.gitter.im/libdatachannel/community.svg)](https://gitter.im/libdatachannel/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Gitter](https://badges.gitter.im/libdatachannel/community.svg)](https://gitter.im/libdatachannel/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Discord](https://img.shields.io/discord/903257095539925006?logo=discord)](https://discord.gg/jXAP8jp3Nn)
 [![Discord](https://img.shields.io/discord/903257095539925006?logo=discord)](https://discord.gg/jXAP8jp3Nn)
 
 
@@ -21,13 +22,13 @@ The WebRTC stack is fully compatible with browsers like Firefox and Chromium, se
 
 
 libdatachannel is licensed under MPL 2.0 since version 0.18, see [LICENSE](https://github.com/paullouisageneau/libdatachannel/blob/master/LICENSE) (previous versions were licensed under LGPLv2.1 or later).
 libdatachannel is licensed under MPL 2.0 since version 0.18, see [LICENSE](https://github.com/paullouisageneau/libdatachannel/blob/master/LICENSE) (previous versions were licensed under LGPLv2.1 or later).
 
 
-libdatachannel is available on [AUR](https://aur.archlinux.org/packages/libdatachannel/), [vcpkg](https://vcpkg.info/port/libdatachannel), and [FreeBSD ports](https://www.freshports.org/www/libdatachannel). Bindings are available for [Rust](https://crates.io/crates/datachannel) and [Node.js](https://www.npmjs.com/package/node-datachannel).
+libdatachannel is available on [AUR](https://aur.archlinux.org/packages/libdatachannel/), [vcpkg](https://vcpkg.io/en/getting-started), and [FreeBSD ports](https://www.freshports.org/www/libdatachannel). Bindings are available for [Rust](https://crates.io/crates/datachannel) and [Node.js](https://www.npmjs.com/package/node-datachannel).
 
 
 ## Dependencies
 ## Dependencies
 
 
 - [GnuTLS](https://www.gnutls.org/), [Mbed TLS](https://www.trustedfirmware.org/projects/mbed-tls/), or [OpenSSL](https://www.openssl.org/)
 - [GnuTLS](https://www.gnutls.org/), [Mbed TLS](https://www.trustedfirmware.org/projects/mbed-tls/), or [OpenSSL](https://www.openssl.org/)
 - [usrsctp](https://github.com/sctplab/usrsctp) (as submodule by default)
 - [usrsctp](https://github.com/sctplab/usrsctp) (as submodule by default)
-- [Plog](https://github.com/SergiusTheBest/plog) (as submodule by default)
+- [plog](https://github.com/SergiusTheBest/plog) (as submodule by default)
 - [libjuice](https://github.com/paullouisageneau/libjuice) (as submodule by default) or [libnice](https://nice.freedesktop.org/) as an ICE backend.
 - [libjuice](https://github.com/paullouisageneau/libjuice) (as submodule by default) or [libnice](https://nice.freedesktop.org/) as an ICE backend.
 - [libsrtp](https://github.com/cisco/libsrtp) (as submodule by default) required if compiled with media support.
 - [libsrtp](https://github.com/cisco/libsrtp) (as submodule by default) required if compiled with media support.
 - [nlohmann JSON](https://github.com/nlohmann/json) (as submodule by default) required to build examples.
 - [nlohmann JSON](https://github.com/nlohmann/json) (as submodule by default) required to build examples.
@@ -178,5 +179,5 @@ Features:
 
 
 ## Thanks
 ## Thanks
 
 
-Thanks to [Streamr](https://streamr.network/), [Vagon](https://vagon.io/), [Deon Botha](https://github.com/dbotha), and [Michael Cho](https://github.com/micoolcho) for sponsoring this work!
+Thanks to [Streamr](https://streamr.network/), [Vagon](https://vagon.io/), [Shiguredo](https://github.com/shiguredo), [Deon Botha](https://github.com/dbotha), and [Michael Cho](https://github.com/micoolcho) for sponsoring this work!
 
 

+ 9 - 0
cmake/version.h.in

@@ -0,0 +1,9 @@
+#ifndef RTC_VERSION_H
+#define RTC_VERSION_H
+
+#define RTC_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
+#define RTC_VERSION_MINOR @PROJECT_VERSION_MINOR@
+#define RTC_VERSION_PATCH @PROJECT_VERSION_PATCH@
+#define RTC_VERSION "@PROJECT_VERSION@"
+
+#endif

+ 1 - 1
deps/json

@@ -1 +1 @@
-Subproject commit 4f8fba14066156b73f1189a2b8bd568bde5284c5
+Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03

+ 1 - 1
deps/libjuice

@@ -1 +1 @@
-Subproject commit 0d8c96650515d625139278d42d6ef4867dc48ce8
+Subproject commit 0b6f958baba55e1a4eb31ec2137f62b2e07382ae

+ 1 - 1
deps/plog

@@ -1 +1 @@
-Subproject commit d8461e9d473e59fbcc1f79eee021550dcf81e618
+Subproject commit e21baecd4753f14da64ede979c5a19302618b752

+ 1 - 1
deps/usrsctp

@@ -1 +1 @@
-Subproject commit 7c31bd35c79ba67084ce029511193a19ceb97447
+Subproject commit ebb18adac6501bad4501b1f6dccb67a1c85cc299

+ 0 - 339
examples/client-benchmark/LICENSE

@@ -1,339 +0,0 @@
-                    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.

+ 0 - 339
examples/client/LICENSE

@@ -1,339 +0,0 @@
-                    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.

+ 2 - 0
examples/client/parse_cl.cpp

@@ -158,6 +158,8 @@ libdatachannel client implementing WebRTC Data Channels with WebSocket signaling
           Web socket server URL or IP address.\n\
           Web socket server URL or IP address.\n\
    [ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
    [ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
           Web socket server port.\n\
           Web socket server port.\n\
+   [ -m ] [ --udpMux ] (type=FLAG)\n\
+          Use UDP multiplex.\n\
    [ -h ] [ --help ] (type=FLAG)\n\
    [ -h ] [ --help ] (type=FLAG)\n\
           Display this help and exit.\n";
           Display this help and exit.\n";
     }
     }

+ 1 - 1
examples/copy-paste-capi/answerer.c

@@ -46,7 +46,7 @@ char *rtcGatheringState_print(rtcGatheringState state);
 int all_space(const char *str);
 int all_space(const char *str);
 
 
 int main(int argc, char **argv) {
 int main(int argc, char **argv) {
-	rtcInitLogger(RTC_LOG_DEBUG, NULL);
+	rtcInitLogger(RTC_LOG_WARNING, NULL);
 
 
 	// Create peer
 	// Create peer
 	rtcConfiguration config;
 	rtcConfiguration config;

+ 1 - 1
examples/copy-paste-capi/offerer.c

@@ -46,7 +46,7 @@ char *rtcGatheringState_print(rtcGatheringState state);
 int all_space(const char *str);
 int all_space(const char *str);
 
 
 int main(int argc, char **argv) {
 int main(int argc, char **argv) {
-	rtcInitLogger(RTC_LOG_DEBUG, NULL);
+	rtcInitLogger(RTC_LOG_WARNING, NULL);
 
 
 	// Create peer
 	// Create peer
 	rtcConfiguration config;
 	rtcConfiguration config;

+ 3 - 4
examples/media-sfu/main.cpp

@@ -49,8 +49,7 @@ int main() {
 
 
 		auto track = pc->addTrack(media);
 		auto track = pc->addTrack(media);
 
 
-		auto session = std::make_shared<rtc::RtcpReceivingSession>();
-		track->setMediaHandler(session);
+		track->setMediaHandler(std::make_shared<rtc::RtcpReceivingSession>());
 
 
 		const rtc::SSRC targetSSRC = 42;
 		const rtc::SSRC targetSSRC = 42;
 		track->onMessage(
 		track->onMessage(
@@ -101,8 +100,8 @@ int main() {
 
 
 			r->track = r->conn->addTrack(media);
 			r->track = r->conn->addTrack(media);
 
 
-			r->track->onOpen([session]() {
-				session->requestKeyframe(); // So the receiver can start playing immediately
+			r->track->onOpen([r]() {
+				r->track->requestKeyframe(); // So the receiver can start playing immediately
 			});
 			});
 			r->track->onMessage([](rtc::binary var) {}, nullptr);
 			r->track->onMessage([](rtc::binary var) {}, nullptr);
 
 

+ 219 - 169
examples/signaling-server-rust/Cargo.lock

@@ -2,6 +2,21 @@
 # It is not intended for manual editing.
 # It is not intended for manual editing.
 version = 3
 version = 3
 
 
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
 [[package]]
 [[package]]
 name = "autocfg"
 name = "autocfg"
 version = "1.1.0"
 version = "1.1.0"
@@ -9,10 +24,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 
 [[package]]
 [[package]]
-name = "base64"
-version = "0.13.0"
+name = "backtrace"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
 
 
 [[package]]
 [[package]]
 name = "bitflags"
 name = "bitflags"
@@ -22,24 +46,33 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 
 [[package]]
 [[package]]
 name = "block-buffer"
 name = "block-buffer"
-version = "0.10.2"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 dependencies = [
 dependencies = [
  "generic-array",
  "generic-array",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "byteorder"
 name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 
 [[package]]
 [[package]]
 name = "bytes"
 name = "bytes"
-version = "1.1.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
 
 
 [[package]]
 [[package]]
 name = "cfg-if"
 name = "cfg-if"
@@ -49,28 +82,34 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 
 [[package]]
 [[package]]
 name = "cpufeatures"
 name = "cpufeatures"
-version = "0.2.2"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
+checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "crypto-common"
 name = "crypto-common"
-version = "0.1.3"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
 dependencies = [
  "generic-array",
  "generic-array",
  "typenum",
  "typenum",
 ]
 ]
 
 
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
 [[package]]
 [[package]]
 name = "digest"
 name = "digest"
-version = "0.10.3"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
 dependencies = [
  "block-buffer",
  "block-buffer",
  "crypto-common",
  "crypto-common",
@@ -84,34 +123,33 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 
 [[package]]
 [[package]]
 name = "form_urlencoded"
 name = "form_urlencoded"
-version = "1.0.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
 dependencies = [
 dependencies = [
- "matches",
  "percent-encoding",
  "percent-encoding",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "futures-channel"
 name = "futures-channel"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
 dependencies = [
 dependencies = [
  "futures-core",
  "futures-core",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "futures-core"
 name = "futures-core"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
 
 
 [[package]]
 [[package]]
 name = "futures-macro"
 name = "futures-macro"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -120,21 +158,21 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "futures-sink"
 name = "futures-sink"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
 
 
 [[package]]
 [[package]]
 name = "futures-task"
 name = "futures-task"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
 
 
 [[package]]
 [[package]]
 name = "futures-util"
 name = "futures-util"
-version = "0.3.21"
+version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
 dependencies = [
 dependencies = [
  "futures-core",
  "futures-core",
  "futures-macro",
  "futures-macro",
@@ -147,9 +185,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "generic-array"
 name = "generic-array"
-version = "0.14.5"
+version = "0.14.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 dependencies = [
 dependencies = [
  "typenum",
  "typenum",
  "version_check",
  "version_check",
@@ -157,29 +195,32 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "getrandom"
 name = "getrandom"
-version = "0.2.6"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
  "libc",
  "libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
+ "wasi",
 ]
 ]
 
 
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
 [[package]]
 [[package]]
 name = "hermit-abi"
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
 
 
 [[package]]
 [[package]]
 name = "http"
 name = "http"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
 dependencies = [
 dependencies = [
  "bytes",
  "bytes",
  "fnv",
  "fnv",
@@ -188,26 +229,25 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "httparse"
 name = "httparse"
-version = "1.7.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
 
 
 [[package]]
 [[package]]
 name = "idna"
 name = "idna"
-version = "0.2.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
 dependencies = [
 dependencies = [
- "matches",
  "unicode-bidi",
  "unicode-bidi",
  "unicode-normalization",
  "unicode-normalization",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "itoa"
 name = "itoa"
-version = "1.0.2"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 
 [[package]]
 [[package]]
 name = "json"
 name = "json"
@@ -217,9 +257,9 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
 
 
 [[package]]
 [[package]]
 name = "libc"
 name = "libc"
-version = "0.2.126"
+version = "0.2.149"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
 
 
 [[package]]
 [[package]]
 name = "libdatachannel_signaling_server_example"
 name = "libdatachannel_signaling_server_example"
@@ -235,9 +275,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "lock_api"
 name = "lock_api"
-version = "0.4.7"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
 dependencies = [
 dependencies = [
  "autocfg",
  "autocfg",
  "scopeguard",
  "scopeguard",
@@ -245,47 +285,55 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "log"
 name = "log"
-version = "0.4.17"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 
 [[package]]
 [[package]]
-name = "matches"
-version = "0.1.9"
+name = "memchr"
+version = "2.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
 
 [[package]]
 [[package]]
-name = "memchr"
-version = "2.5.0"
+name = "miniz_oxide"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
 
 
 [[package]]
 [[package]]
 name = "mio"
 name = "mio"
-version = "0.8.3"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
- "log",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
  "windows-sys",
  "windows-sys",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "num_cpus"
 name = "num_cpus"
-version = "1.13.1"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
 dependencies = [
  "hermit-abi",
  "hermit-abi",
  "libc",
  "libc",
 ]
 ]
 
 
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 [[package]]
 name = "parking_lot"
 name = "parking_lot"
 version = "0.12.1"
 version = "0.12.1"
@@ -298,28 +346,28 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "parking_lot_core"
 name = "parking_lot_core"
-version = "0.9.3"
+version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
  "libc",
  "libc",
  "redox_syscall",
  "redox_syscall",
  "smallvec",
  "smallvec",
- "windows-sys",
+ "windows-targets",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "percent-encoding"
 name = "percent-encoding"
-version = "2.1.0"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
 
 
 [[package]]
 [[package]]
 name = "pin-project-lite"
 name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
 
 
 [[package]]
 [[package]]
 name = "pin-utils"
 name = "pin-utils"
@@ -329,24 +377,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 
 [[package]]
 [[package]]
 name = "ppv-lite86"
 name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 
 [[package]]
 [[package]]
 name = "proc-macro2"
 name = "proc-macro2"
-version = "1.0.39"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
 dependencies = [
 dependencies = [
  "unicode-ident",
  "unicode-ident",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "quote"
 name = "quote"
-version = "1.0.18"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
 ]
 ]
@@ -374,33 +422,39 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "rand_core"
 name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
 dependencies = [
  "getrandom",
  "getrandom",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "redox_syscall"
 name = "redox_syscall"
-version = "0.2.13"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
 dependencies = [
  "bitflags",
  "bitflags",
 ]
 ]
 
 
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
 [[package]]
 [[package]]
 name = "scopeguard"
 name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 
 [[package]]
 [[package]]
-name = "sha-1"
-version = "0.10.0"
+name = "sha1"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
 dependencies = [
 dependencies = [
  "cfg-if",
  "cfg-if",
  "cpufeatures",
  "cpufeatures",
@@ -409,40 +463,43 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "signal-hook-registry"
 name = "signal-hook-registry"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "slab"
 name = "slab"
-version = "0.4.6"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
 
 
 [[package]]
 [[package]]
 name = "smallvec"
 name = "smallvec"
-version = "1.8.0"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
 
 
 [[package]]
 [[package]]
 name = "socket2"
 name = "socket2"
-version = "0.4.4"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
- "winapi",
+ "windows-sys",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "syn"
 name = "syn"
-version = "1.0.96"
+version = "2.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -451,18 +508,18 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "thiserror"
 name = "thiserror"
-version = "1.0.31"
+version = "1.0.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
 dependencies = [
 dependencies = [
  "thiserror-impl",
  "thiserror-impl",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "thiserror-impl"
 name = "thiserror-impl"
-version = "1.0.31"
+version = "1.0.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -480,20 +537,19 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tinyvec_macros"
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 
 [[package]]
 [[package]]
 name = "tokio"
 name = "tokio"
-version = "1.20.4"
+version = "1.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb78f30e4b41e98ca4cce5acb51168a033839a7af9e42b380355808e14e98ee0"
+checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
 dependencies = [
 dependencies = [
- "autocfg",
+ "backtrace",
  "bytes",
  "bytes",
  "libc",
  "libc",
- "memchr",
  "mio",
  "mio",
  "num_cpus",
  "num_cpus",
  "parking_lot",
  "parking_lot",
@@ -501,14 +557,14 @@ dependencies = [
  "signal-hook-registry",
  "signal-hook-registry",
  "socket2",
  "socket2",
  "tokio-macros",
  "tokio-macros",
- "winapi",
+ "windows-sys",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "tokio-macros"
 name = "tokio-macros"
-version = "1.8.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -517,9 +573,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tokio-tungstenite"
 name = "tokio-tungstenite"
-version = "0.17.1"
+version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
+checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
 dependencies = [
 dependencies = [
  "futures-util",
  "futures-util",
  "log",
  "log",
@@ -529,18 +585,18 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tungstenite"
 name = "tungstenite"
-version = "0.17.2"
+version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
+checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
 dependencies = [
 dependencies = [
- "base64",
  "byteorder",
  "byteorder",
  "bytes",
  "bytes",
+ "data-encoding",
  "http",
  "http",
  "httparse",
  "httparse",
  "log",
  "log",
  "rand",
  "rand",
- "sha-1",
+ "sha1",
  "thiserror",
  "thiserror",
  "url",
  "url",
  "utf-8",
  "utf-8",
@@ -548,40 +604,39 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "typenum"
 name = "typenum"
-version = "1.15.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 
 [[package]]
 [[package]]
 name = "unicode-bidi"
 name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
 
 
 [[package]]
 [[package]]
 name = "unicode-ident"
 name = "unicode-ident"
-version = "1.0.0"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 
 [[package]]
 [[package]]
 name = "unicode-normalization"
 name = "unicode-normalization"
-version = "0.1.19"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
 dependencies = [
  "tinyvec",
  "tinyvec",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "url"
 name = "url"
-version = "2.2.2"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
 dependencies = [
 dependencies = [
  "form_urlencoded",
  "form_urlencoded",
  "idna",
  "idna",
- "matches",
  "percent-encoding",
  "percent-encoding",
 ]
 ]
 
 
@@ -597,12 +652,6 @@ version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 
-[[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
 [[package]]
 [[package]]
 name = "wasi"
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -610,66 +659,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 
 [[package]]
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "windows-sys"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "windows-targets",
 ]
 ]
 
 
 [[package]]
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-sys"
-version = "0.36.1"
+name = "windows-targets"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
 dependencies = [
+ "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
  "windows_aarch64_msvc",
  "windows_i686_gnu",
  "windows_i686_gnu",
  "windows_i686_msvc",
  "windows_i686_msvc",
  "windows_x86_64_gnu",
  "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
  "windows_x86_64_msvc",
  "windows_x86_64_msvc",
 ]
 ]
 
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
 [[package]]
 [[package]]
 name = "windows_aarch64_msvc"
 name = "windows_aarch64_msvc"
-version = "0.36.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 
 [[package]]
 [[package]]
 name = "windows_i686_gnu"
 name = "windows_i686_gnu"
-version = "0.36.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 
 [[package]]
 [[package]]
 name = "windows_i686_msvc"
 name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 
 [[package]]
 [[package]]
 name = "windows_x86_64_gnu"
 name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 
 [[package]]
 [[package]]
 name = "windows_x86_64_msvc"
 name = "windows_x86_64_msvc"
-version = "0.36.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

+ 3 - 2
examples/streamer/fileparser.cpp

@@ -50,8 +50,9 @@ void FileParser::loadNextSample() {
         return;
         return;
     }
     }
 
 
-    vector<uint8_t> fileContents((std::istreambuf_iterator<char>(source)), std::istreambuf_iterator<char>());
-    sample = *reinterpret_cast<vector<byte> *>(&fileContents);
+    vector<char> contents((std::istreambuf_iterator<char>(source)), std::istreambuf_iterator<char>());
+    auto *b = reinterpret_cast<const std::byte*>(contents.data());
+    sample.assign(b, b + contents.size());
     sampleTime_us += sampleDuration_us;
     sampleTime_us += sampleDuration_us;
 }
 }
 
 

+ 1 - 1
examples/streamer/helpers.cpp

@@ -38,7 +38,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
 		x.LowPart = filetime.dwLowDateTime;
 		x.LowPart = filetime.dwLowDateTime;
 		x.HighPart = filetime.dwHighDateTime;
 		x.HighPart = filetime.dwHighDateTime;
 		usec = x.QuadPart / 10 - epoch_offset_us;
 		usec = x.QuadPart / 10 - epoch_offset_us;
-		tv->tv_sec = time_t(usec / 1000000ULL);
+		tv->tv_sec = long(usec / 1000000ULL);
 		tv->tv_usec = long(usec % 1000000ULL);
 		tv->tv_usec = long(usec % 1000000ULL);
 	}
 	}
 	if (tz) {
 	if (tz) {

+ 8 - 12
examples/streamer/main.cpp

@@ -210,17 +210,15 @@ shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const
     // create RTP configuration
     // create RTP configuration
     auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate);
     auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate);
     // create packetizer
     // create packetizer
-    auto packetizer = make_shared<H264RtpPacketizer>(H264RtpPacketizer::Separator::Length, rtpConfig);
-    // create H264 handler
-    auto h264Handler = make_shared<H264PacketizationHandler>(packetizer);
+    auto packetizer = make_shared<H264RtpPacketizer>(NalUnit::Separator::Length, rtpConfig);
     // add RTCP SR handler
     // add RTCP SR handler
     auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
     auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
-    h264Handler->addToChain(srReporter);
+    packetizer->addToChain(srReporter);
     // add RTCP NACK handler
     // add RTCP NACK handler
     auto nackResponder = make_shared<RtcpNackResponder>();
     auto nackResponder = make_shared<RtcpNackResponder>();
-    h264Handler->addToChain(nackResponder);
+    packetizer->addToChain(nackResponder);
     // set handler
     // set handler
-    track->setMediaHandler(h264Handler);
+    track->setMediaHandler(packetizer);
     track->onOpen(onOpen);
     track->onOpen(onOpen);
     auto trackData = make_shared<ClientTrackData>(track, srReporter);
     auto trackData = make_shared<ClientTrackData>(track, srReporter);
     return trackData;
     return trackData;
@@ -232,19 +230,17 @@ shared_ptr<ClientTrackData> addAudio(const shared_ptr<PeerConnection> pc, const
     audio.addSSRC(ssrc, cname, msid, cname);
     audio.addSSRC(ssrc, cname, msid, cname);
     auto track = pc->addTrack(audio);
     auto track = pc->addTrack(audio);
     // create RTP configuration
     // create RTP configuration
-    auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, OpusRtpPacketizer::defaultClockRate);
+    auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, OpusRtpPacketizer::DefaultClockRate);
     // create packetizer
     // create packetizer
     auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig);
     auto packetizer = make_shared<OpusRtpPacketizer>(rtpConfig);
-    // create opus handler
-    auto opusHandler = make_shared<OpusPacketizationHandler>(packetizer);
     // add RTCP SR handler
     // add RTCP SR handler
     auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
     auto srReporter = make_shared<RtcpSrReporter>(rtpConfig);
-    opusHandler->addToChain(srReporter);
+    packetizer->addToChain(srReporter);
     // add RTCP NACK handler
     // add RTCP NACK handler
     auto nackResponder = make_shared<RtcpNackResponder>();
     auto nackResponder = make_shared<RtcpNackResponder>();
-    opusHandler->addToChain(nackResponder);
+    packetizer->addToChain(nackResponder);
     // set handler
     // set handler
-    track->setMediaHandler(opusHandler);
+    track->setMediaHandler(packetizer);
     track->onOpen(onOpen);
     track->onOpen(onOpen);
     auto trackData = make_shared<ClientTrackData>(track, srReporter);
     auto trackData = make_shared<ClientTrackData>(track, srReporter);
     return trackData;
     return trackData;

+ 1 - 1
examples/streamer/stream.hpp

@@ -26,7 +26,7 @@ public:
 	virtual rtc::binary getSample() = 0;
 	virtual rtc::binary getSample() = 0;
 };
 };
 
 
-class Stream: std::enable_shared_from_this<Stream> {
+class Stream: public std::enable_shared_from_this<Stream> {
     uint64_t startTime = 0;
     uint64_t startTime = 0;
     std::mutex mutex;
     std::mutex mutex;
     DispatchQueue dispatchQueue = DispatchQueue("StreamQueue");
     DispatchQueue dispatchQueue = DispatchQueue("StreamQueue");

+ 56 - 0
include/rtc/av1rtppacketizer.hpp

@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2023 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_AV1_RTP_PACKETIZER_H
+#define RTC_AV1_RTP_PACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandler.hpp"
+#include "nalunit.hpp"
+#include "rtppacketizer.hpp"
+
+namespace rtc {
+
+// RTP packetization of AV1 payload
+class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer {
+public:
+	// Default clock rate for AV1 in RTP
+	inline static const uint32_t defaultClockRate = 90 * 1000;
+
+	// Define how OBUs are seperated in a AV1 Sample
+	enum class Packetization {
+		Obu = RTC_OBU_PACKETIZED_OBU,
+		TemporalUnit = RTC_OBU_PACKETIZED_TEMPORAL_UNIT,
+	};
+
+	// Constructs AV1 payload packetizer with given RTP configuration.
+	// @note RTP configuration is used in packetization process which may change some configuration
+	// properties such as sequence number.
+	AV1RtpPacketizer(Packetization packetization, shared_ptr<RtpPacketizationConfig> rtpConfig,
+	                 uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
+	void outgoing(message_vector &messages, const message_callback &send) override;
+
+private:
+	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+	std::vector<shared_ptr<binary>> packetizeObu(binary_ptr message, uint16_t maxFragmentSize);
+
+	const uint16_t maxFragmentSize;
+	const Packetization packetization;
+	std::shared_ptr<binary> sequenceHeader;
+};
+
+// For backward compatibility, do not use
+using AV1PacketizationHandler [[deprecated("Add AV1RtpPacketizer directly")]] = PacketizationHandler;
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_AV1_RTP_PACKETIZER_H */

+ 5 - 5
include/rtc/candidate.hpp

@@ -67,11 +67,11 @@ private:
 	uint16_t mPort;
 	uint16_t mPort;
 };
 };
 
 
-} // namespace rtc
-
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Candidate &candidate);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Candidate::Type &type);
 RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
 RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
-                                        const rtc::Candidate::TransportType &transportType);
+                                        const Candidate::TransportType &transportType);
+
+} // namespace rtc
 
 
 #endif
 #endif

+ 29 - 0
include/rtc/configuration.hpp

@@ -88,6 +88,35 @@ struct RTC_CPP_EXPORT Configuration {
 	optional<size_t> maxMessageSize;
 	optional<size_t> maxMessageSize;
 };
 };
 
 
+#ifdef RTC_ENABLE_WEBSOCKET
+
+struct WebSocketConfiguration {
+	bool disableTlsVerification = false; // if true, don't verify the TLS certificate
+	optional<ProxyServer> proxyServer;   // only non-authenticated http supported for now
+	std::vector<string> protocols;
+	optional<std::chrono::milliseconds> connectionTimeout; // zero to disable
+	optional<std::chrono::milliseconds> pingInterval;      // zero to disable
+	optional<int> maxOutstandingPings;
+	optional<string> caCertificatePemFile;
+	optional<string> certificatePemFile;
+	optional<string> keyPemFile;
+	optional<string> keyPemPass;
+	optional<size_t> maxMessageSize;
+};
+
+struct WebSocketServerConfiguration {
+	uint16_t port = 8080;
+	bool enableTls = false;
+	optional<string> certificatePemFile;
+	optional<string> keyPemFile;
+	optional<string> keyPemPass;
+	optional<string> bindAddress;
+	optional<std::chrono::milliseconds> connectionTimeout;
+	optional<size_t> maxMessageSize;
+};
+
+#endif
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif

+ 45 - 24
include/rtc/description.hpp

@@ -28,6 +28,17 @@ const string DEFAULT_OPUS_AUDIO_PROFILE =
 const string DEFAULT_H264_VIDEO_PROFILE =
 const string DEFAULT_H264_VIDEO_PROFILE =
     "profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1";
     "profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1";
 
 
+struct CertificateFingerprint {
+	enum class Algorithm { Sha1, Sha224, Sha256, Sha384, Sha512 };
+	static string AlgorithmIdentifier(Algorithm algorithm);
+	static size_t AlgorithmSize(Algorithm algorithm);
+
+	bool isValid() const;
+
+	Algorithm algorithm;
+	string value;
+};
+
 class RTC_CPP_EXPORT Description {
 class RTC_CPP_EXPORT Description {
 public:
 public:
 	enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
 	enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
@@ -51,11 +62,11 @@ public:
 	std::vector<string> iceOptions() const;
 	std::vector<string> iceOptions() const;
 	optional<string> iceUfrag() const;
 	optional<string> iceUfrag() const;
 	optional<string> icePwd() const;
 	optional<string> icePwd() const;
-	optional<string> fingerprint() const;
+	optional<CertificateFingerprint> fingerprint() const;
 	bool ended() const;
 	bool ended() const;
 
 
 	void hintType(Type type);
 	void hintType(Type type);
-	void setFingerprint(string fingerprint);
+	void setFingerprint(CertificateFingerprint f);
 	void addIceOption(string option);
 	void addIceOption(string option);
 	void removeIceOption(const string &option);
 	void removeIceOption(const string &option);
 
 
@@ -78,19 +89,20 @@ public:
 	public:
 	public:
 		virtual ~Entry() = default;
 		virtual ~Entry() = default;
 
 
-		virtual string type() const { return mType; }
-		virtual string description() const { return mDescription; }
-		virtual string mid() const { return mMid; }
+		virtual string type() const;
+		virtual string description() const;
+		virtual string mid() const;
 
 
-		Direction direction() const { return mDirection; }
+		Direction direction() const;
 		void setDirection(Direction dir);
 		void setDirection(Direction dir);
 
 
-		bool isRemoved() const { return mIsRemoved; }
+		bool isRemoved() const;
 		void markRemoved();
 		void markRemoved();
 
 
 		std::vector<string> attributes() const;
 		std::vector<string> attributes() const;
 		void addAttribute(string attr);
 		void addAttribute(string attr);
 		void removeAttribute(const string &attr);
 		void removeAttribute(const string &attr);
+		void addRid(string rid);
 
 
 		struct RTC_CPP_EXPORT ExtMap {
 		struct RTC_CPP_EXPORT ExtMap {
 			static int parseId(string_view description);
 			static int parseId(string_view description);
@@ -108,6 +120,7 @@ public:
 
 
 		std::vector<int> extIds();
 		std::vector<int> extIds();
 		ExtMap *extMap(int id);
 		ExtMap *extMap(int id);
+		const ExtMap *extMap(int id) const;
 		void addExtMap(ExtMap map);
 		void addExtMap(ExtMap map);
 		void removeExtMap(int id);
 		void removeExtMap(int id);
 
 
@@ -119,6 +132,7 @@ public:
 
 
 	protected:
 	protected:
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
+
 		virtual string generateSdpLines(string_view eol) const;
 		virtual string generateSdpLines(string_view eol) const;
 
 
 		std::vector<string> mAttributes;
 		std::vector<string> mAttributes;
@@ -128,6 +142,7 @@ public:
 		string mType;
 		string mType;
 		string mDescription;
 		string mDescription;
 		string mMid;
 		string mMid;
+		std::vector<string> mRids;
 		Direction mDirection;
 		Direction mDirection;
 		bool mIsRemoved;
 		bool mIsRemoved;
 	};
 	};
@@ -141,12 +156,12 @@ public:
 		string description() const override;
 		string description() const override;
 		Application reciprocate() const;
 		Application reciprocate() const;
 
 
-		void setSctpPort(uint16_t port) { mSctpPort = port; }
-		void hintSctpPort(uint16_t port) { mSctpPort = mSctpPort.value_or(port); }
-		void setMaxMessageSize(size_t size) { mMaxMessageSize = size; }
+		void setSctpPort(uint16_t port);
+		void hintSctpPort(uint16_t port);
+		void setMaxMessageSize(size_t size);
 
 
-		optional<uint16_t> sctpPort() const { return mSctpPort; }
-		optional<size_t> maxMessageSize() const { return mMaxMessageSize; }
+		optional<uint16_t> sctpPort() const;
+		optional<size_t> maxMessageSize() const;
 
 
 		virtual void parseSdpLine(string_view line) override;
 		virtual void parseSdpLine(string_view line) override;
 
 
@@ -175,7 +190,7 @@ public:
 		bool hasSSRC(uint32_t ssrc) const;
 		bool hasSSRC(uint32_t ssrc) const;
 		void clearSSRCs();
 		void clearSSRCs();
 		std::vector<uint32_t> getSSRCs() const;
 		std::vector<uint32_t> getSSRCs() const;
-		std::optional<std::string> getCNameForSsrc(uint32_t ssrc) const;
+		optional<std::string> getCNameForSsrc(uint32_t ssrc) const;
 
 
 		int bitrate() const;
 		int bitrate() const;
 		void setBitrate(int bitrate);
 		void setBitrate(int bitrate);
@@ -205,6 +220,7 @@ public:
 		bool hasPayloadType(int payloadType) const;
 		bool hasPayloadType(int payloadType) const;
 		std::vector<int> payloadTypes() const;
 		std::vector<int> payloadTypes() const;
 		RtpMap *rtpMap(int payloadType);
 		RtpMap *rtpMap(int payloadType);
+		const RtpMap *rtpMap(int payloadType) const;
 		void addRtpMap(RtpMap map);
 		void addRtpMap(RtpMap map);
 		void removeRtpMap(int payloadType);
 		void removeRtpMap(int payloadType);
 		void removeFormat(const string &format);
 		void removeFormat(const string &format);
@@ -228,12 +244,15 @@ public:
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
 
 
 		void addAudioCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
 		void addAudioCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
-
 		void addOpusCodec(int payloadType, optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
 		void addOpusCodec(int payloadType, optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
-
 		void addPCMACodec(int payloadType, optional<string> profile = std::nullopt);
 		void addPCMACodec(int payloadType, optional<string> profile = std::nullopt);
-
 		void addPCMUCodec(int payloadType, optional<string> profile = std::nullopt);
 		void addPCMUCodec(int payloadType, optional<string> profile = std::nullopt);
+		void addAACCodec(int payloadType, optional<string> profile = std::nullopt);
+
+		[[deprecated("Use addAACCodec")]] inline void
+		addAacCodec(int payloadType, optional<string> profile = std::nullopt) {
+			addAACCodec(payloadType, std::move(profile));
+		};
 	};
 	};
 
 
 	class RTC_CPP_EXPORT Video : public Media {
 	class RTC_CPP_EXPORT Video : public Media {
@@ -243,8 +262,10 @@ public:
 		void addVideoCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
 		void addVideoCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
 
 
 		void addH264Codec(int payloadType, optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
 		void addH264Codec(int payloadType, optional<string> profile = DEFAULT_H264_VIDEO_PROFILE);
-		void addVP8Codec(int payloadType);
-		void addVP9Codec(int payloadType);
+		void addH265Codec(int payloadType, optional<string> profile = std::nullopt);
+		void addVP8Codec(int payloadType, optional<string> profile = std::nullopt);
+		void addVP9Codec(int payloadType, optional<string> profile = std::nullopt);
+		void addAV1Codec(int payloadType, optional<string> profile = std::nullopt);
 	};
 	};
 
 
 	bool hasApplication() const;
 	bool hasApplication() const;
@@ -281,7 +302,7 @@ private:
 	string mSessionId;
 	string mSessionId;
 	std::vector<string> mIceOptions;
 	std::vector<string> mIceOptions;
 	optional<string> mIceUfrag, mIcePwd;
 	optional<string> mIceUfrag, mIcePwd;
-	optional<string> mFingerprint;
+	optional<CertificateFingerprint> mFingerprint;
 	std::vector<string> mAttributes; // other attributes
 	std::vector<string> mAttributes; // other attributes
 
 
 	// Entries
 	// Entries
@@ -293,11 +314,11 @@ private:
 	bool mEnded = false;
 	bool mEnded = false;
 };
 };
 
 
-} // namespace rtc
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Description &description);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, Description::Type type);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, Description::Role role);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const Description::Direction &direction);
 
 
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description::Direction &direction);
+} // namespace rtc
 
 
 #endif
 #endif

+ 23 - 0
include/rtc/frameinfo.hpp

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2019-2020 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_FRAMEINFO_H
+#define RTC_FRAMEINFO_H
+
+#include "common.hpp"
+
+namespace rtc {
+
+struct RTC_CPP_EXPORT FrameInfo {
+	FrameInfo(uint32_t timestamp) : timestamp(timestamp){};
+	uint32_t timestamp = 0; // RTP Timestamp
+};
+
+} // namespace rtc
+
+#endif // RTC_FRAMEINFO_H

+ 3 - 3
include/rtc/global.hpp

@@ -12,8 +12,8 @@
 #include "common.hpp"
 #include "common.hpp"
 
 
 #include <chrono>
 #include <chrono>
-#include <iostream>
 #include <future>
 #include <future>
+#include <iostream>
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -52,8 +52,8 @@ struct SctpSettings {
 
 
 RTC_CPP_EXPORT void SetSctpSettings(SctpSettings s);
 RTC_CPP_EXPORT void SetSctpSettings(SctpSettings s);
 
 
-} // namespace rtc
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level);
 
 
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::LogLevel level);
+} // namespace rtc
 
 
 #endif
 #endif

+ 0 - 32
include/rtc/h264packetizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_H264_PACKETIZATION_HANDLER_H
-#define RTC_H264_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "h264rtppacketizer.hpp"
-#include "mediachainablehandler.hpp"
-#include "nalunit.hpp"
-
-namespace rtc {
-
-/// Handler for H264 packetization
-class RTC_CPP_EXPORT H264PacketizationHandler final : public MediaChainableHandler {
-public:
-	/// Construct handler for H264 packetization.
-	/// @param packetizer RTP packetizer for h264
-	H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_H264_PACKETIZATION_HANDLER_H */

+ 43 - 0
include/rtc/h264rtpdepacketizer.hpp

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020 Staz Modrzynski
+ * Copyright (c) 2020 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_H264_RTP_DEPACKETIZER_H
+#define RTC_H264_RTP_DEPACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "common.hpp"
+#include "mediahandler.hpp"
+#include "message.hpp"
+#include "rtp.hpp"
+
+#include <iterator>
+
+namespace rtc {
+
+/// RTP depacketization for H264
+class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler {
+public:
+	H264RtpDepacketizer() = default;
+	virtual ~H264RtpDepacketizer() = default;
+
+	void incoming(message_vector &messages, const message_callback &send) override;
+
+private:
+	std::vector<message_ptr> mRtpBuffer;
+
+	message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt,
+	                           uint32_t timestamp);
+};
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif /* RTC_H264_RTP_DEPACKETIZER_H */

+ 22 - 25
include/rtc/h264rtppacketizer.hpp

@@ -1,5 +1,6 @@
 /**
 /**
  * Copyright (c) 2020 Filip Klembara (in2core)
  * Copyright (c) 2020 Filip Klembara (in2core)
+ * Copyright (c) 2023 Paul-Louis Ageneau
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,49 +12,45 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-#include "mediahandlerrootelement.hpp"
 #include "nalunit.hpp"
 #include "nalunit.hpp"
 #include "rtppacketizer.hpp"
 #include "rtppacketizer.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-/// RTP packetization of h264 payload
-class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer,
-                                               public MediaHandlerRootElement {
-	shared_ptr<NalUnits> splitMessage(binary_ptr message);
-	const uint16_t maximumFragmentSize;
-
+/// RTP packetization for H264
+class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer {
 public:
 public:
+	using Separator = NalUnit::Separator;
+
 	/// Default clock rate for H264 in RTP
 	/// Default clock rate for H264 in RTP
 	inline static const uint32_t defaultClockRate = 90 * 1000;
 	inline static const uint32_t defaultClockRate = 90 * 1000;
 
 
-	/// NAL unit separator
-	enum class Separator {
-		Length = RTC_NAL_SEPARATOR_LENGTH, // first 4 bytes are NAL unit length
-		LongStartSequence = RTC_NAL_SEPARATOR_LONG_START_SEQUENCE,   // 0x00, 0x00, 0x00, 0x01
-		ShortStartSequence = RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE, // 0x00, 0x00, 0x01
-		StartSequence = RTC_NAL_SEPARATOR_START_SEQUENCE, // LongStartSequence or ShortStartSequence
-	};
-
-	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 maximumFragmentSize maximum size of one NALU fragment
-	H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                  uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+	/// @param separator NAL unit separator
+	/// @param rtpConfig RTP configuration
+	/// @param maxFragmentSize maximum size of one NALU fragment
+	H264RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
+	                  uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
 
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
+	// For backward compatibility, do not use
+	[[deprecated]] H264RtpPacketizer(
+	    shared_ptr<RtpPacketizationConfig> rtpConfig,
+	    uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
+	void outgoing(message_vector &messages, const message_callback &send) override;
 
 
 private:
 private:
+	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+
+	const uint16_t maxFragmentSize;
 	const Separator separator;
 	const Separator separator;
 };
 };
 
 
+// For backward compatibility, do not use
+using H264PacketizationHandler [[deprecated("Add H264RtpPacketizer directly")]] = PacketizationHandler;
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif /* RTC_ENABLE_MEDIA */
 #endif /* RTC_ENABLE_MEDIA */

+ 186 - 0
include/rtc/h265nalunit.hpp

@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2023 Zita Liao (Dolby)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_H265_NAL_UNIT_H
+#define RTC_H265_NAL_UNIT_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "common.hpp"
+#include "nalunit.hpp"
+
+#include <cassert>
+
+namespace rtc {
+
+#pragma pack(push, 1)
+
+#define H265_FU_HEADER_SIZE 1
+/// Nalu header
+struct RTC_CPP_EXPORT H265NalUnitHeader {
+	/*
+	* nal_unit_header( ) {
+	* forbidden_zero_bit	f(1)
+	* nal_unit_type			u(6)
+	* nuh_layer_id			u(6)
+	* nuh_temporal_id_plus1	u(3)
+	}
+	*/
+	uint8_t _first = 0;  // high byte of header
+	uint8_t _second = 0; // low byte of header
+
+	bool forbiddenBit() const { return _first >> 7; }
+	uint8_t unitType() const { return (_first & 0b0111'1110) >> 1; }
+	uint8_t nuhLayerId() const { return ((_first & 0x1) << 5) | ((_second & 0b1111'1000) >> 3); }
+	uint8_t nuhTempIdPlus1() const { return _second & 0b111; }
+
+	void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
+	void setUnitType(uint8_t type) { _first = (_first & 0b1000'0001) | ((type & 0b11'1111) << 1); }
+	void setNuhLayerId(uint8_t nuhLayerId) {
+		_first = (_first & 0b1111'1110) | ((nuhLayerId & 0b10'0000) >> 5);
+		_second = (_second & 0b0000'0111) | ((nuhLayerId & 0b01'1111) << 3);
+	}
+	void setNuhTempIdPlus1(uint8_t nuhTempIdPlus1) {
+		_second = (_second & 0b1111'1000) | (nuhTempIdPlus1 & 0b111);
+	}
+};
+
+/// Nalu fragment header
+struct RTC_CPP_EXPORT H265NalUnitFragmentHeader {
+	/*
+	 * +---------------+
+	 * |0|1|2|3|4|5|6|7|
+	 * +-+-+-+-+-+-+-+-+
+	 * |S|E|  FuType   |
+	 * +---------------+
+	 */
+	uint8_t _first = 0;
+
+	bool isStart() const { return _first >> 7; }
+	bool isEnd() const { return (_first >> 6) & 0x01; }
+	uint8_t unitType() const { return _first & 0b11'1111; }
+
+	void setStart(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
+	void setEnd(bool isSet) { _first = (_first & 0b1011'1111) | (isSet << 6); }
+	void setUnitType(uint8_t type) { _first = (_first & 0b1100'0000) | (type & 0b11'1111); }
+};
+
+#pragma pack(pop)
+
+/// Nal unit
+struct RTC_CPP_EXPORT H265NalUnit : NalUnit {
+	H265NalUnit(const H265NalUnit &unit) = default;
+	H265NalUnit(size_t size, bool includingHeader = true)
+	    : NalUnit(size, includingHeader, NalUnit::Type::H265) {}
+	H265NalUnit(binary &&data) : NalUnit(std::move(data)) {}
+	H265NalUnit() : NalUnit(NalUnit::Type::H265) {}
+
+	template <typename Iterator>
+	H265NalUnit(Iterator begin_, Iterator end_) : NalUnit(begin_, end_) {}
+
+	bool forbiddenBit() const { return header()->forbiddenBit(); }
+	uint8_t unitType() const { return header()->unitType(); }
+	uint8_t nuhLayerId() const { return header()->nuhLayerId(); }
+	uint8_t nuhTempIdPlus1() const { return header()->nuhTempIdPlus1(); }
+
+	binary payload() const {
+		assert(size() >= H265_NAL_HEADER_SIZE);
+		return {begin() + H265_NAL_HEADER_SIZE, end()};
+	}
+
+	void setForbiddenBit(bool isSet) { header()->setForbiddenBit(isSet); }
+	void setUnitType(uint8_t type) { header()->setUnitType(type); }
+	void setNuhLayerId(uint8_t nuhLayerId) { header()->setNuhLayerId(nuhLayerId); }
+	void setNuhTempIdPlus1(uint8_t nuhTempIdPlus1) { header()->setNuhTempIdPlus1(nuhTempIdPlus1); }
+
+	void setPayload(binary payload) {
+		assert(size() >= H265_NAL_HEADER_SIZE);
+		erase(begin() + H265_NAL_HEADER_SIZE, end());
+		insert(end(), payload.begin(), payload.end());
+	}
+
+protected:
+	const H265NalUnitHeader *header() const {
+		assert(size() >= H265_NAL_HEADER_SIZE);
+		return reinterpret_cast<const H265NalUnitHeader *>(data());
+	}
+
+	H265NalUnitHeader *header() {
+		assert(size() >= H265_NAL_HEADER_SIZE);
+		return reinterpret_cast<H265NalUnitHeader *>(data());
+	}
+};
+
+/// Nal unit fragment A
+struct RTC_CPP_EXPORT H265NalUnitFragment : H265NalUnit {
+	static std::vector<shared_ptr<H265NalUnitFragment>> fragmentsFrom(shared_ptr<H265NalUnit> nalu,
+	                                                                  uint16_t maxFragmentSize);
+
+	enum class FragmentType { Start, Middle, End };
+
+	H265NalUnitFragment(FragmentType type, bool forbiddenBit, uint8_t nuhLayerId,
+	                    uint8_t nuhTempIdPlus1, uint8_t unitType, binary data);
+
+	uint8_t unitType() const { return fragmentHeader()->unitType(); }
+
+	binary payload() const {
+		assert(size() >= H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+		return {begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE, end()};
+	}
+
+	FragmentType type() const {
+		if (fragmentHeader()->isStart()) {
+			return FragmentType::Start;
+		} else if (fragmentHeader()->isEnd()) {
+			return FragmentType::End;
+		} else {
+			return FragmentType::Middle;
+		}
+	}
+
+	void setUnitType(uint8_t type) { fragmentHeader()->setUnitType(type); }
+
+	void setPayload(binary payload) {
+		assert(size() >= H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+		erase(begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE, end());
+		insert(end(), payload.begin(), payload.end());
+	}
+
+	void setFragmentType(FragmentType type);
+
+protected:
+	const uint8_t nal_type_fu = 49;
+
+	H265NalUnitHeader *fragmentIndicator() { return reinterpret_cast<H265NalUnitHeader *>(data()); }
+
+	const H265NalUnitHeader *fragmentIndicator() const {
+		return reinterpret_cast<const H265NalUnitHeader *>(data());
+	}
+
+	H265NalUnitFragmentHeader *fragmentHeader() {
+		return reinterpret_cast<H265NalUnitFragmentHeader *>(data() + H265_NAL_HEADER_SIZE);
+	}
+
+	const H265NalUnitFragmentHeader *fragmentHeader() const {
+		return reinterpret_cast<const H265NalUnitFragmentHeader *>(data() + H265_NAL_HEADER_SIZE);
+	}
+};
+
+class RTC_CPP_EXPORT H265NalUnits : public std::vector<shared_ptr<H265NalUnit>> {
+public:
+	static const uint16_t defaultMaximumFragmentSize =
+	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6
+
+	std::vector<shared_ptr<binary>> generateFragments(uint16_t maxFragmentSize);
+};
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_NAL_UNIT_H */

+ 56 - 0
include/rtc/h265rtppacketizer.hpp

@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2023 Zita Liao (Dolby)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_H265_RTP_PACKETIZER_H
+#define RTC_H265_RTP_PACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "h265nalunit.hpp"
+#include "rtppacketizer.hpp"
+
+namespace rtc {
+
+// RTP packetization for H265
+class RTC_CPP_EXPORT H265RtpPacketizer final : public RtpPacketizer {
+public:
+	using Separator = NalUnit::Separator;
+
+	// Default clock rate for H265 in RTP
+	inline static const uint32_t defaultClockRate = 90 * 1000;
+
+	// Constructs h265 payload packetizer with given RTP configuration.
+	// @note RTP configuration is used in packetization process which may change some configuration
+	// properties such as sequence number.
+	// @param separator NAL unit separator
+	// @param rtpConfig  RTP configuration
+	// @param maxFragmentSize maximum size of one NALU fragment
+	H265RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
+	                  uint16_t maxFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
+
+	// for backward compatibility
+	[[deprecated]] H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+	                  uint16_t maxFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
+
+	void outgoing(message_vector &messages, const message_callback &send) override;
+
+private:
+	shared_ptr<H265NalUnits> splitMessage(binary_ptr message);
+
+	const uint16_t maxFragmentSize;
+	const NalUnit::Separator separator;
+};
+
+// For backward compatibility, do not use
+using H265PacketizationHandler [[deprecated("Add H265RtpPacketizer directly")]] = PacketizationHandler;
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_H265_RTP_PACKETIZER_H */

+ 0 - 48
include/rtc/mediachainablehandler.hpp

@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#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;
-	mutable std::mutex mutex;
-
-	message_ptr handleIncomingBinary(message_ptr);
-	message_ptr handleIncomingControl(message_ptr);
-	message_ptr handleOutgoingBinary(message_ptr);
-	message_ptr handleOutgoingControl(message_ptr);
-	bool sendProduct(ChainedOutgoingProduct product);
-	shared_ptr<MediaHandlerElement> getLeaf() const;
-
-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

+ 35 - 17
include/rtc/mediahandler.hpp

@@ -11,28 +11,46 @@
 #define RTC_MEDIA_HANDLER_H
 #define RTC_MEDIA_HANDLER_H
 
 
 #include "common.hpp"
 #include "common.hpp"
+#include "description.hpp"
 #include "message.hpp"
 #include "message.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-class RTC_CPP_EXPORT MediaHandler {
-protected:
-	// Use this callback when trying to send custom data (such as RTCP) to the client.
-	synchronized_callback<message_ptr> outgoingCallback;
-
+class RTC_CPP_EXPORT MediaHandler : public std::enable_shared_from_this<MediaHandler> {
 public:
 public:
-	// Called when there is traffic coming from the peer
-	virtual message_ptr incoming(message_ptr ptr) = 0;
-
-	// Called when there is traffic that needs to be sent to the peer
-	virtual message_ptr outgoing(message_ptr ptr) = 0;
-
-	// This callback is used to send traffic back to the peer.
-	void onOutgoing(const std::function<void(message_ptr)> &cb) {
-		this->outgoingCallback = synchronized_callback<message_ptr>(cb);
-	}
-
-	virtual bool requestKeyframe() { return false; }
+	MediaHandler();
+	virtual ~MediaHandler();
+
+	/// Called when a media is added or updated
+	/// @param desc Description of the media
+	virtual void media([[maybe_unused]] const Description::Media &desc) {}
+
+	/// Called when there is traffic coming from the peer
+	/// @param messages Incoming messages from the peer, can be modified by the handler
+	/// @param send Send callback to send messages back to the peer
+	virtual void incoming([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {}
+
+	/// Called when there is traffic that needs to be sent to the peer
+	/// @param messages Outgoing messages to the peer, can be modified by the handler
+	/// @param send Send callback to send messages back to the peer
+	virtual void outgoing([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {}
+
+	virtual bool requestKeyframe(const message_callback &send);
+	virtual bool requestBitrate(unsigned int bitrate, const message_callback &send);
+
+	void addToChain(shared_ptr<MediaHandler> handler);
+	void setNext(shared_ptr<MediaHandler> handler);
+	shared_ptr<MediaHandler> next();
+	shared_ptr<const MediaHandler> next() const;
+	shared_ptr<MediaHandler> last();             // never null
+	shared_ptr<const MediaHandler> last() const; // never null
+
+	void mediaChain(const Description::Media &desc);
+	void incomingChain(message_vector &messages, const message_callback &send);
+	void outgoingChain(message_vector &messages, const message_callback &send);
+
+private:
+	shared_ptr<MediaHandler> mNext;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 0 - 112
include/rtc/mediahandlerelement.hpp

@@ -1,112 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#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

+ 0 - 36
include/rtc/mediahandlerrootelement.hpp

@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_MEDIA_HANDLER_ROOT_ELEMENT_H
-#define RTC_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 // RTC_MEDIA_HANDLER_ROOT_ELEMENT_H

+ 10 - 2
include/rtc/message.hpp

@@ -10,6 +10,7 @@
 #define RTC_MESSAGE_H
 #define RTC_MESSAGE_H
 
 
 #include "common.hpp"
 #include "common.hpp"
+#include "frameinfo.hpp"
 #include "reliability.hpp"
 #include "reliability.hpp"
 
 
 #include <functional>
 #include <functional>
@@ -32,10 +33,12 @@ struct RTC_CPP_EXPORT Message : binary {
 	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
 	shared_ptr<Reliability> reliability;
 	shared_ptr<Reliability> reliability;
+	shared_ptr<FrameInfo> frameInfo;
 };
 };
 
 
 using message_ptr = 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_vector = std::vector<message_ptr>;
 
 
 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;
@@ -43,10 +46,12 @@ 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, shared_ptr<Reliability> reliability = nullptr) {
+                         unsigned int stream = 0, shared_ptr<Reliability> reliability = nullptr,
+                         shared_ptr<FrameInfo> frameInfo = 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;
+	message->frameInfo = frameInfo;
 	return message;
 	return message;
 }
 }
 
 
@@ -56,7 +61,10 @@ RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Messag
 
 
 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,
-                                        shared_ptr<Reliability> reliability = nullptr);
+                                        shared_ptr<Reliability> reliability = nullptr,
+                                        shared_ptr<FrameInfo> frameInfo = nullptr);
+
+RTC_CPP_EXPORT message_ptr make_message(size_t size, message_ptr orig);
 
 
 RTC_CPP_EXPORT message_ptr make_message(message_variant data);
 RTC_CPP_EXPORT message_ptr make_message(message_variant data);
 
 

+ 73 - 4
include/rtc/nalunit.hpp

@@ -25,6 +25,7 @@ struct RTC_CPP_EXPORT NalUnitHeader {
 
 
 	bool forbiddenBit() const { return _first >> 7; }
 	bool forbiddenBit() const { return _first >> 7; }
 	uint8_t nri() const { return _first >> 5 & 0x03; }
 	uint8_t nri() const { return _first >> 5 & 0x03; }
+	uint8_t idc() const { return _first & 0x60; }
 	uint8_t unitType() const { return _first & 0x1F; }
 	uint8_t unitType() const { return _first & 0x1F; }
 
 
 	void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
 	void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
@@ -49,12 +50,29 @@ struct RTC_CPP_EXPORT NalUnitFragmentHeader {
 
 
 #pragma pack(pop)
 #pragma pack(pop)
 
 
+enum NalUnitStartSequenceMatch {
+	NUSM_noMatch,
+	NUSM_firstZero,
+	NUSM_secondZero,
+	NUSM_thirdZero,
+	NUSM_shortMatch,
+	NUSM_longMatch
+};
+
+static const size_t H264_NAL_HEADER_SIZE = 1;
+static const size_t H265_NAL_HEADER_SIZE = 2;
 /// Nal unit
 /// Nal unit
 struct RTC_CPP_EXPORT NalUnit : binary {
 struct RTC_CPP_EXPORT NalUnit : binary {
+	enum class Type { H264, H265 };
+
 	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, Type type = Type::H264)
+	    : binary(size + (includingHeader
+	                         ? 0
+	                         : (type == Type::H264 ? H264_NAL_HEADER_SIZE : H265_NAL_HEADER_SIZE))) {}
 	NalUnit(binary &&data) : binary(std::move(data)) {}
 	NalUnit(binary &&data) : binary(std::move(data)) {}
-	NalUnit() : binary(1) {}
+	NalUnit(Type type = Type::H264)
+	    : binary(type == Type::H264 ? H264_NAL_HEADER_SIZE : H265_NAL_HEADER_SIZE) {}
 	template <typename Iterator> NalUnit(Iterator begin_, Iterator end_) : binary(begin_, end_) {}
 	template <typename Iterator> NalUnit(Iterator begin_, Iterator end_) : binary(begin_, end_) {}
 
 
 	bool forbiddenBit() const { return header()->forbiddenBit(); }
 	bool forbiddenBit() const { return header()->forbiddenBit(); }
@@ -76,6 +94,57 @@ struct RTC_CPP_EXPORT NalUnit : binary {
 		insert(end(), payload.begin(), payload.end());
 		insert(end(), payload.begin(), payload.end());
 	}
 	}
 
 
+	/// NAL unit separator
+	enum class Separator {
+		Length = RTC_NAL_SEPARATOR_LENGTH, // first 4 bytes are NAL unit length
+		LongStartSequence = RTC_NAL_SEPARATOR_LONG_START_SEQUENCE,   // 0x00, 0x00, 0x00, 0x01
+		ShortStartSequence = RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE, // 0x00, 0x00, 0x01
+		StartSequence = RTC_NAL_SEPARATOR_START_SEQUENCE, // LongStartSequence or ShortStartSequence
+	};
+
+	static NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match,
+	                                                        std::byte _byte, Separator separator) {
+		assert(separator != Separator::Length);
+		auto byte = (uint8_t)_byte;
+		auto detectShort =
+		    separator == Separator::ShortStartSequence || separator == Separator::StartSequence;
+		auto detectLong =
+		    separator == Separator::LongStartSequence || separator == 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 == 0x00 && detectShort) {
+				return NUSM_secondZero;
+			} else if (byte == 0x01 && detectShort) {
+				return NUSM_shortMatch;
+			}
+			break;
+		case NUSM_thirdZero:
+			if (byte == 0x00 && detectLong) {
+				return NUSM_thirdZero;
+			} else if (byte == 0x01 && detectLong) {
+				return NUSM_longMatch;
+			}
+			break;
+		case NUSM_shortMatch:
+			return NUSM_shortMatch;
+		case NUSM_longMatch:
+			return NUSM_longMatch;
+		}
+		return NUSM_noMatch;
+	}
+
 protected:
 protected:
 	const NalUnitHeader *header() const {
 	const NalUnitHeader *header() const {
 		assert(size() >= 1);
 		assert(size() >= 1);
@@ -91,7 +160,7 @@ protected:
 /// Nal unit fragment A
 /// Nal unit fragment A
 struct RTC_CPP_EXPORT NalUnitFragmentA : NalUnit {
 struct RTC_CPP_EXPORT NalUnitFragmentA : NalUnit {
 	static std::vector<shared_ptr<NalUnitFragmentA>> fragmentsFrom(shared_ptr<NalUnit> nalu,
 	static std::vector<shared_ptr<NalUnitFragmentA>> fragmentsFrom(shared_ptr<NalUnit> nalu,
-	                                                               uint16_t maximumFragmentSize);
+	                                                               uint16_t maxFragmentSize);
 
 
 	enum class FragmentType { Start, Middle, End };
 	enum class FragmentType { Start, Middle, End };
 
 
@@ -148,7 +217,7 @@ public:
 	static const uint16_t defaultMaximumFragmentSize =
 	static const uint16_t defaultMaximumFragmentSize =
 	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6
 	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6
 
 
-	std::vector<shared_ptr<binary>> generateFragments(uint16_t maximumFragmentSize);
+	std::vector<shared_ptr<binary>> generateFragments(uint16_t maxFragmentSize);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 0 - 32
include/rtc/opuspacketizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_OPUS_PACKETIZATION_HANDLER_H
-#define RTC_OPUS_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediachainablehandler.hpp"
-#include "opusrtppacketizer.hpp"
-
-namespace rtc {
-
-/// Handler for opus packetization
-class RTC_CPP_EXPORT OpusPacketizationHandler final : public MediaChainableHandler {
-
-public:
-	/// Construct handler for opus packetization.
-	/// @param packetizer RTP packetizer for opus
-	OpusPacketizationHandler(shared_ptr<OpusRtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_OPUS_PACKETIZATION_HANDLER_H */

+ 0 - 50
include/rtc/opusrtppacketizer.hpp

@@ -1,50 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_OPUS_RTP_PACKETIZER_H
-#define RTC_OPUS_RTP_PACKETIZER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandlerrootelement.hpp"
-#include "rtppacketizer.hpp"
-
-namespace rtc {
-
-/// RTP packetizer for opus
-class RTC_CPP_EXPORT OpusRtpPacketizer final : public RtpPacketizer,
-                                               public MediaHandlerRootElement {
-public:
-	/// default clock rate used in opus RTP communication
-	inline static const uint32_t defaultClockRate = 48 * 1000;
-
-	/// Constructs opus packetizer with given RTP configuration.
-	/// @note RTP configuration is used in packetization process which may change some configuration
-	/// properties such as sequence number.
-	/// @param rtpConfig  RTP configuration
-	OpusRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig);
-
-	/// Creates RTP packet for given payload based on `rtpConfig`.
-	/// @note This function increase sequence number after packetization.
-	/// @param payload RTP payload
-	/// @param setMark This needs to be `false` for all RTP packets with opus payload
-	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
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_OPUS_RTP_PACKETIZER_H */

+ 20 - 7
include/rtc/peerconnection.hpp

@@ -46,6 +46,16 @@ public:
 		Closed = RTC_CLOSED
 		Closed = RTC_CLOSED
 	};
 	};
 
 
+	enum class IceState : int {
+		New = RTC_ICE_NEW,
+		Checking = RTC_ICE_CHECKING,
+		Connected = RTC_ICE_CONNECTED,
+		Completed = RTC_ICE_COMPLETED,
+		Failed = RTC_ICE_FAILED,
+		Disconnected = RTC_ICE_DISCONNECTED,
+		Closed = RTC_ICE_CLOSED
+	};
+
 	enum class GatheringState : int {
 	enum class GatheringState : int {
 		New = RTC_GATHERING_NEW,
 		New = RTC_GATHERING_NEW,
 		InProgress = RTC_GATHERING_INPROGRESS,
 		InProgress = RTC_GATHERING_INPROGRESS,
@@ -68,11 +78,13 @@ public:
 
 
 	const Configuration *config() const;
 	const Configuration *config() const;
 	State state() const;
 	State state() const;
+	IceState iceState() const;
 	GatheringState gatheringState() const;
 	GatheringState gatheringState() const;
 	SignalingState signalingState() const;
 	SignalingState signalingState() const;
 	bool hasMedia() const;
 	bool hasMedia() const;
 	optional<Description> localDescription() const;
 	optional<Description> localDescription() const;
 	optional<Description> remoteDescription() const;
 	optional<Description> remoteDescription() const;
+	size_t remoteMaxMessageSize() const;
 	optional<string> localAddress() const;
 	optional<string> localAddress() const;
 	optional<string> remoteAddress() const;
 	optional<string> remoteAddress() const;
 	uint16_t maxDataChannelId() const;
 	uint16_t maxDataChannelId() const;
@@ -85,7 +97,8 @@ public:
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
 	shared_ptr<MediaHandler> getMediaHandler();
 	shared_ptr<MediaHandler> getMediaHandler();
 
 
-	[[nodiscard]] shared_ptr<DataChannel> createDataChannel(string label, DataChannelInit init = {});
+	[[nodiscard]] shared_ptr<DataChannel> createDataChannel(string label,
+	                                                        DataChannelInit init = {});
 	void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
 	void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
 
 
 	[[nodiscard]] shared_ptr<Track> addTrack(Description::Media description);
 	[[nodiscard]] shared_ptr<Track> addTrack(Description::Media description);
@@ -94,6 +107,7 @@ public:
 	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);
+	void onIceStateChange(std::function<void(IceState state)> callback);
 	void onGatheringStateChange(std::function<void(GatheringState state)> callback);
 	void onGatheringStateChange(std::function<void(GatheringState state)> callback);
 	void onSignalingStateChange(std::function<void(SignalingState state)> callback);
 	void onSignalingStateChange(std::function<void(SignalingState state)> callback);
 
 
@@ -106,12 +120,11 @@ public:
 	optional<std::chrono::milliseconds> rtt();
 	optional<std::chrono::milliseconds> rtt();
 };
 };
 
 
-} // namespace rtc
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::State state);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::IceState state);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::GatheringState state);
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, PeerConnection::SignalingState state);
 
 
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
-                                        rtc::PeerConnection::GatheringState state);
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
-                                        rtc::PeerConnection::SignalingState state);
+} // namespace rtc
 
 
 #endif
 #endif

+ 36 - 0
include/rtc/plihandler.hpp

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2023 Arda Cinar
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_PLI_RESPONDER_H
+#define RTC_PLI_RESPONDER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandler.hpp"
+#include "utils.hpp"
+
+namespace rtc {
+
+/// Responds to PLI and FIR messages sent by the receiver. The sender should respond to these
+/// messages by sending an intra.
+class RTC_CPP_EXPORT PliHandler final : public MediaHandler {
+    rtc::synchronized_callback<> mOnPli;
+
+public:
+	/// Constructs the PLIResponder object to notify whenever a new intra frame is requested
+	/// @param onPli The callback that gets called whenever an intra frame is requested by the receiver
+    PliHandler(std::function<void(void)> onPli);
+
+	void incoming(message_vector &messages, const message_callback &send) override;
+};
+
+}
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif // RTC_PLI_RESPONDER_H

+ 18 - 3
include/rtc/reliability.hpp

@@ -16,10 +16,25 @@
 namespace rtc {
 namespace rtc {
 
 
 struct Reliability {
 struct Reliability {
-	enum class Type { Reliable = 0, Rexmit, Timed };
-
-	Type type = Type::Reliable;
+	// It true, the channel does not enforce message ordering and out-of-order delivery is allowed
 	bool unordered = false;
 	bool unordered = false;
+
+	// If both maxPacketLifeTime or maxRetransmits are unset, the channel is reliable.
+	// If either maxPacketLifeTime or maxRetransmits is set, the channel is unreliable.
+	// (The settings are exclusive so both maxPacketLifetime and maxRetransmits must not be set.)
+
+	// Time window during which transmissions and retransmissions may occur
+	optional<std::chrono::milliseconds> maxPacketLifeTime;
+
+	// Maximum number of retransmissions that are attempted
+	optional<unsigned int> maxRetransmits;
+
+	// For backward compatibility, do not use
+	enum class Type { Reliable = 0, Rexmit, Timed };
+	union {
+		Type typeDeprecated = Type::Reliable;
+		[[deprecated("Use maxPacketLifeTime or maxRetransmits")]] Type type;
+	};
 	variant<int, std::chrono::milliseconds> rexmit = 0;
 	variant<int, std::chrono::milliseconds> rexmit = 0;
 };
 };
 
 

+ 111 - 34
include/rtc/rtc.h

@@ -9,10 +9,15 @@
 #ifndef RTC_C_API
 #ifndef RTC_C_API
 #define RTC_C_API
 #define RTC_C_API
 
 
+#include "version.h"
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+#include <stdbool.h>
+#include <stdint.h>
+
 #ifdef RTC_STATIC
 #ifdef RTC_STATIC
 #define RTC_C_EXPORT
 #define RTC_C_EXPORT
 #else // dynamic library
 #else // dynamic library
@@ -27,16 +32,6 @@ extern "C" {
 #endif
 #endif
 #endif
 #endif
 
 
-#ifdef _WIN32
-#ifdef CAPI_STDCALL
-#define RTC_API __stdcall
-#else
-#define RTC_API
-#endif
-#else // not WIN32
-#define RTC_API
-#endif
-
 #ifndef RTC_ENABLE_WEBSOCKET
 #ifndef RTC_ENABLE_WEBSOCKET
 #define RTC_ENABLE_WEBSOCKET 1
 #define RTC_ENABLE_WEBSOCKET 1
 #endif
 #endif
@@ -48,13 +43,30 @@ extern "C" {
 #define RTC_DEFAULT_MTU 1280 // IPv6 minimum guaranteed MTU
 #define RTC_DEFAULT_MTU 1280 // IPv6 minimum guaranteed MTU
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
-#define RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE                                                          \
-	((uint16_t)(RTC_DEFAULT_MTU - 12 - 8 - 40)) // SRTP/UDP/IPv6
-#define RTC_DEFAULT_MAXIMUM_PACKET_COUNT_FOR_NACK_CACHE ((unsigned)512)
+#define RTC_DEFAULT_MAX_FRAGMENT_SIZE ((uint16_t)(RTC_DEFAULT_MTU - 12 - 8 - 40)) // SRTP/UDP/IPv6
+#define RTC_DEFAULT_MAX_STORED_PACKET_COUNT 512
+// Deprecated, do not use
+#define RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE RTC_DEFAULT_MAX_FRAGMENT_SIZE
+#define RTC_DEFAULT_MAXIMUM_PACKET_COUNT_FOR_NACK_CACHE RTC_DEFAULT_MAX_STORED_PACKET_COUNT
 #endif
 #endif
 
 
-#include <stdbool.h>
-#include <stdint.h>
+#ifdef _WIN32
+#ifdef CAPI_STDCALL
+#define RTC_API __stdcall
+#else
+#define RTC_API
+#endif
+#else // not WIN32
+#define RTC_API
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define RTC_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#define RTC_DEPRECATED __declspec(deprecated)
+#else
+#define DEPRECATED
+#endif
 
 
 // libdatachannel C API
 // libdatachannel C API
 
 
@@ -67,6 +79,16 @@ typedef enum {
 	RTC_CLOSED = 5
 	RTC_CLOSED = 5
 } rtcState;
 } rtcState;
 
 
+typedef enum {
+	RTC_ICE_NEW = 0,
+	RTC_ICE_CHECKING = 1,
+	RTC_ICE_CONNECTED = 2,
+	RTC_ICE_COMPLETED = 3,
+	RTC_ICE_FAILED = 4,
+	RTC_ICE_DISCONNECTED = 5,
+	RTC_ICE_CLOSED = 6
+} rtcIceState;
+
 typedef enum {
 typedef enum {
 	RTC_GATHERING_NEW = 0,
 	RTC_GATHERING_NEW = 0,
 	RTC_GATHERING_INPROGRESS = 1,
 	RTC_GATHERING_INPROGRESS = 1,
@@ -102,11 +124,14 @@ typedef enum {
 	RTC_CODEC_H264 = 0,
 	RTC_CODEC_H264 = 0,
 	RTC_CODEC_VP8 = 1,
 	RTC_CODEC_VP8 = 1,
 	RTC_CODEC_VP9 = 2,
 	RTC_CODEC_VP9 = 2,
+	RTC_CODEC_H265 = 3,
+	RTC_CODEC_AV1 = 4,
 
 
 	// audio
 	// audio
 	RTC_CODEC_OPUS = 128,
 	RTC_CODEC_OPUS = 128,
 	RTC_CODEC_PCMU = 129,
 	RTC_CODEC_PCMU = 129,
-	RTC_CODEC_PCMA = 130
+	RTC_CODEC_PCMA = 130,
+	RTC_CODEC_AAC = 131,
 } rtcCodec;
 } rtcCodec;
 
 
 typedef enum {
 typedef enum {
@@ -131,6 +156,7 @@ typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const
 typedef void(RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid,
 typedef void(RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid,
                                                 void *ptr);
                                                 void *ptr);
 typedef void(RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState state, void *ptr);
 typedef void(RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState state, void *ptr);
+typedef void(RTC_API *rtcIceStateChangeCallbackFunc)(int pc, rtcIceState state, void *ptr);
 typedef void(RTC_API *rtcGatheringStateCallbackFunc)(int pc, rtcGatheringState state, void *ptr);
 typedef void(RTC_API *rtcGatheringStateCallbackFunc)(int pc, rtcGatheringState state, void *ptr);
 typedef void(RTC_API *rtcSignalingStateCallbackFunc)(int pc, rtcSignalingState state, void *ptr);
 typedef void(RTC_API *rtcSignalingStateCallbackFunc)(int pc, rtcSignalingState state, void *ptr);
 typedef void(RTC_API *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr);
 typedef void(RTC_API *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr);
@@ -143,6 +169,7 @@ typedef void *(RTC_API *rtcInterceptorCallbackFunc)(int pc, const char *message,
                                                     void *ptr);
                                                     void *ptr);
 typedef void(RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr);
 typedef void(RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr);
 typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr);
 typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr);
+typedef void(RTC_API *rtcPliHandlerCallbackFunc)(int tr, void *ptr);
 
 
 // Log
 // Log
 
 
@@ -179,6 +206,7 @@ RTC_C_EXPORT int rtcDeletePeerConnection(int pc);
 RTC_C_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
 RTC_C_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
 RTC_C_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
 RTC_C_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
 RTC_C_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
 RTC_C_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
+RTC_C_EXPORT int rtcSetIceStateChangeCallback(int pc, rtcIceStateChangeCallbackFunc cb);
 RTC_C_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb);
 RTC_C_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb);
 RTC_C_EXPORT int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb);
 RTC_C_EXPORT int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb);
 
 
@@ -199,6 +227,7 @@ RTC_C_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize,
                                              int remoteSize);
                                              int remoteSize);
 
 
 RTC_C_EXPORT int rtcGetMaxDataChannelStream(int pc);
 RTC_C_EXPORT int rtcGetMaxDataChannelStream(int pc);
+RTC_C_EXPORT int rtcGetRemoteMaxMessageSize(int pc);
 
 
 // DataChannel, Track, and WebSocket common API
 // DataChannel, Track, and WebSocket common API
 
 
@@ -212,6 +241,7 @@ RTC_C_EXPORT int rtcDelete(int id);
 RTC_C_EXPORT bool rtcIsOpen(int id);
 RTC_C_EXPORT bool rtcIsOpen(int id);
 RTC_C_EXPORT bool rtcIsClosed(int id);
 RTC_C_EXPORT bool rtcIsClosed(int id);
 
 
+RTC_C_EXPORT int rtcMaxMessageSize(int id);
 RTC_C_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send
 RTC_C_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send
 RTC_C_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
 RTC_C_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
 RTC_C_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
 RTC_C_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
@@ -227,8 +257,8 @@ RTC_C_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
 typedef struct {
 typedef struct {
 	bool unordered;
 	bool unordered;
 	bool unreliable;
 	bool unreliable;
-	int maxPacketLifeTime; // ignored if reliable
-	int maxRetransmits;    // ignored if reliable
+	unsigned int maxPacketLifeTime; // ignored if reliable
+	unsigned int maxRetransmits;    // ignored if reliable
 } rtcReliability;
 } rtcReliability;
 
 
 typedef struct {
 typedef struct {
@@ -261,6 +291,7 @@ typedef struct {
 	const char *name;    // optional
 	const char *name;    // optional
 	const char *msid;    // optional
 	const char *msid;    // optional
 	const char *trackId; // optional, track ID used in MSID
 	const char *trackId; // optional, track ID used in MSID
+	const char *profile; // optional, codec profile
 } rtcTrackInit;
 } rtcTrackInit;
 
 
 RTC_C_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb);
 RTC_C_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb);
@@ -272,11 +303,20 @@ RTC_C_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
 RTC_C_EXPORT int rtcGetTrackMid(int tr, char *buffer, int size);
 RTC_C_EXPORT int rtcGetTrackMid(int tr, char *buffer, int size);
 RTC_C_EXPORT int rtcGetTrackDirection(int tr, rtcDirection *direction);
 RTC_C_EXPORT int rtcGetTrackDirection(int tr, rtcDirection *direction);
 
 
+RTC_C_EXPORT int rtcRequestKeyframe(int tr);
+RTC_C_EXPORT int rtcRequestBitrate(int tr, unsigned int bitrate);
+
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
 // Media
 // Media
 
 
-// Define how NAL units are separated in a H264 sample
+// Define how OBUs are packetizied in a AV1 Sample
+typedef enum {
+	RTC_OBU_PACKETIZED_OBU = 0,
+	RTC_OBU_PACKETIZED_TEMPORAL_UNIT = 1,
+} rtcObuPacketization;
+
+// Define how NAL units are separated in a H264/H265 sample
 typedef enum {
 typedef enum {
 	RTC_NAL_SEPARATOR_LENGTH = 0,               // first 4 bytes are NAL unit length
 	RTC_NAL_SEPARATOR_LENGTH = 0,               // first 4 bytes are NAL unit length
 	RTC_NAL_SEPARATOR_LONG_START_SEQUENCE = 1,  // 0x00, 0x00, 0x00, 0x01
 	RTC_NAL_SEPARATOR_LONG_START_SEQUENCE = 1,  // 0x00, 0x00, 0x00, 0x01
@@ -292,11 +332,19 @@ typedef struct {
 	uint16_t sequenceNumber;
 	uint16_t sequenceNumber;
 	uint32_t timestamp;
 	uint32_t timestamp;
 
 
-	// H264
+	// H264, H265, AV1
+	uint16_t maxFragmentSize; // Maximum fragment size, 0 means default
+
+	// H264/H265 only
 	rtcNalUnitSeparator nalSeparator; // NAL unit separator
 	rtcNalUnitSeparator nalSeparator; // NAL unit separator
-	uint16_t maxFragmentSize;         // Maximum NAL unit fragment size
 
 
-} rtcPacketizationHandlerInit;
+	// AV1 only
+	rtcObuPacketization obuPacketization; // OBU paketization for AV1 samples
+
+} rtcPacketizerInit;
+
+// Deprecated, do not use
+typedef rtcPacketizerInit rtcPacketizationHandlerInit;
 
 
 typedef struct {
 typedef struct {
 	uint32_t ssrc;
 	uint32_t ssrc;
@@ -305,8 +353,6 @@ typedef struct {
 	const char *trackId; // optional, track ID used in MSID
 	const char *trackId; // optional, track ID used in MSID
 } rtcSsrcForTypeInit;
 } rtcSsrcForTypeInit;
 
 
-// Opaque message
-
 // Opaque type used (via rtcMessage*) to reference an rtc::Message
 // Opaque type used (via rtcMessage*) to reference an rtc::Message
 typedef void *rtcMessage;
 typedef void *rtcMessage;
 
 
@@ -316,21 +362,50 @@ typedef void *rtcMessage;
 RTC_C_EXPORT rtcMessage *rtcCreateOpaqueMessage(void *data, int size);
 RTC_C_EXPORT rtcMessage *rtcCreateOpaqueMessage(void *data, int size);
 RTC_C_EXPORT void rtcDeleteOpaqueMessage(rtcMessage *msg);
 RTC_C_EXPORT void rtcDeleteOpaqueMessage(rtcMessage *msg);
 
 
-// Set MediaInterceptor for peer connection
+// Set MediaInterceptor on peer connection
 RTC_C_EXPORT int rtcSetMediaInterceptorCallback(int id, rtcInterceptorCallbackFunc cb);
 RTC_C_EXPORT int rtcSetMediaInterceptorCallback(int id, rtcInterceptorCallbackFunc cb);
 
 
-// Set H264PacketizationHandler for track
-RTC_C_EXPORT int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init);
-
-// Set OpusPacketizationHandler for track
-RTC_C_EXPORT int rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init);
-
-// Chain RtcpSrReporter to handler chain for given track
+// Set a packetizer on track
+RTC_C_EXPORT int rtcSetH264Packetizer(int tr, const rtcPacketizerInit *init);
+RTC_C_EXPORT int rtcSetH265Packetizer(int tr, const rtcPacketizerInit *init);
+RTC_C_EXPORT int rtcSetAV1Packetizer(int tr, const rtcPacketizerInit *init);
+RTC_C_EXPORT int rtcSetOpusPacketizer(int tr, const rtcPacketizerInit *init);
+RTC_C_EXPORT int rtcSetAACPacketizer(int tr, const rtcPacketizerInit *init);
+
+// Deprecated, do not use
+RTC_DEPRECATED static inline int
+rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+	return rtcSetH264Packetizer(tr, init);
+}
+RTC_DEPRECATED static inline int
+rtcSetH265PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+	return rtcSetH265Packetizer(tr, init);
+}
+RTC_DEPRECATED static inline int
+rtcSetAV1PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+	return rtcSetAV1Packetizer(tr, init);
+}
+RTC_DEPRECATED static inline int
+rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+	return rtcSetOpusPacketizer(tr, init);
+}
+RTC_DEPRECATED static inline int
+rtcSetAACPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+	return rtcSetAACPacketizer(tr, init);
+}
+
+// Chain RtcpReceivingSession on track
+RTC_C_EXPORT int rtcChainRtcpReceivingSession(int tr);
+
+// Chain RtcpSrReporter on track
 RTC_C_EXPORT int rtcChainRtcpSrReporter(int tr);
 RTC_C_EXPORT int rtcChainRtcpSrReporter(int tr);
 
 
-// Chain RtcpNackResponder to handler chain for given track
+// Chain RtcpNackResponder on track
 RTC_C_EXPORT int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount);
 RTC_C_EXPORT int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount);
 
 
+// Chain PliHandler on track
+RTC_C_EXPORT int rtcChainPliHandler(int tr, rtcPliHandlerCallbackFunc cb);
+
 // Transform seconds to timestamp using track's clock rate, result is written to timestamp
 // Transform seconds to timestamp using track's clock rate, result is written to timestamp
 RTC_C_EXPORT int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp);
 RTC_C_EXPORT int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp);
 
 
@@ -380,6 +455,7 @@ typedef struct {
 	int connectionTimeoutMs; // in milliseconds, 0 means default, < 0 means disabled
 	int connectionTimeoutMs; // in milliseconds, 0 means default, < 0 means disabled
 	int pingIntervalMs;      // in milliseconds, 0 means default, < 0 means disabled
 	int pingIntervalMs;      // in milliseconds, 0 means default, < 0 means disabled
 	int maxOutstandingPings; // 0 means default, < 0 means disabled
 	int maxOutstandingPings; // 0 means default, < 0 means disabled
+	int maxMessageSize;      // <= 0 means default
 } rtcWsConfiguration;
 } rtcWsConfiguration;
 
 
 RTC_C_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id
 RTC_C_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id
@@ -399,8 +475,9 @@ typedef struct {
 	const char *certificatePemFile; // NULL for autogenerated certificate
 	const char *certificatePemFile; // NULL for autogenerated certificate
 	const char *keyPemFile;         // NULL for autogenerated certificate
 	const char *keyPemFile;         // NULL for autogenerated certificate
 	const char *keyPemPass;         // NULL if no pass
 	const char *keyPemPass;         // NULL if no pass
-	const char *bindAddress;        // NULL for IP_ANY_ADDR
+	const char *bindAddress;        // NULL for any
 	int connectionTimeoutMs;        // in milliseconds, 0 means default, < 0 means disabled
 	int connectionTimeoutMs;        // in milliseconds, 0 means default, < 0 means disabled
+	int maxMessageSize;             // <= 0 means default
 } rtcWsServerConfiguration;
 } rtcWsServerConfiguration;
 
 
 RTC_C_EXPORT int rtcCreateWebSocketServer(const rtcWsServerConfiguration *config,
 RTC_C_EXPORT int rtcCreateWebSocketServer(const rtcWsServerConfiguration *config,

+ 9 - 6
include/rtc/rtc.hpp

@@ -27,14 +27,17 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-// Media handling
-#include "mediachainablehandler.hpp"
+// Media
+#include "av1rtppacketizer.hpp"
+#include "h264rtppacketizer.hpp"
+#include "h264rtpdepacketizer.hpp"
+#include "h265rtppacketizer.hpp"
+#include "mediahandler.hpp"
+#include "plihandler.hpp"
 #include "rtcpnackresponder.hpp"
 #include "rtcpnackresponder.hpp"
 #include "rtcpreceivingsession.hpp"
 #include "rtcpreceivingsession.hpp"
 #include "rtcpsrreporter.hpp"
 #include "rtcpsrreporter.hpp"
-
-// Opus/h264 streaming
-#include "h264packetizationhandler.hpp"
-#include "opuspacketizationhandler.hpp"
+#include "rtppacketizer.hpp"
+#include "rtpdepacketizer.hpp"
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA

+ 17 - 27
include/rtc/rtcpnackresponder.hpp

@@ -11,16 +11,24 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-#include "mediahandlerelement.hpp"
+#include "mediahandler.hpp"
 
 
 #include <queue>
 #include <queue>
 #include <unordered_map>
 #include <unordered_map>
 
 
 namespace rtc {
 namespace rtc {
 
 
-class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
+class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandler {
+public:
+	static const size_t DefaultMaxSize = 512;
+
+	RtcpNackResponder(size_t maxSize = DefaultMaxSize);
 
 
-	/// Packet storage
+	void incoming(message_vector &messages, const message_callback &send) override;
+	void outgoing(message_vector &messages, const message_callback &send) override;
+
+private:
+	// Packet storage
 	class RTC_CPP_EXPORT Storage {
 	class RTC_CPP_EXPORT Storage {
 
 
 		/// Packet storage element
 		/// Packet storage element
@@ -37,20 +45,18 @@ class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
 		shared_ptr<Element> oldest = nullptr;
 		shared_ptr<Element> oldest = nullptr;
 		/// Newest packet in storage
 		/// Newest packet in storage
 		shared_ptr<Element> newest = nullptr;
 		shared_ptr<Element> newest = nullptr;
-
 		/// Inner storage
 		/// Inner storage
 		std::unordered_map<uint16_t, shared_ptr<Element>> storage{};
 		std::unordered_map<uint16_t, shared_ptr<Element>> storage{};
+		std::mutex mutex;
 
 
 		/// Maximum storage size
 		/// Maximum storage size
-		const unsigned maximumSize;
+		const size_t maxSize;
 
 
-		/// Returnst current size
-		unsigned size();
+		/// Returns current size
+		size_t size();
 
 
 	public:
 	public:
-		static const unsigned defaultMaximumSize = 512;
-
-		Storage(unsigned _maximumSize);
+		Storage(size_t _maxSize);
 
 
 		/// Returns packet with given sequence number
 		/// Returns packet with given sequence number
 		optional<binary_ptr> get(uint16_t sequenceNumber);
 		optional<binary_ptr> get(uint16_t sequenceNumber);
@@ -60,23 +66,7 @@ class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
 		void store(binary_ptr 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;
+	const shared_ptr<Storage> mStorage;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 15 - 10
include/rtc/rtcpreceivingsession.hpp

@@ -17,29 +17,34 @@
 #include "message.hpp"
 #include "message.hpp"
 #include "rtp.hpp"
 #include "rtp.hpp"
 
 
+#include <atomic>
+
 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 MediaHandler {
 class RTC_CPP_EXPORT RtcpReceivingSession : public MediaHandler {
 public:
 public:
-	message_ptr incoming(message_ptr ptr) override;
-	message_ptr outgoing(message_ptr ptr) override;
-	bool send(message_ptr ptr);
+	RtcpReceivingSession() = default;
+	virtual ~RtcpReceivingSession() = default;
 
 
-	void requestBitrate(unsigned int newBitrate);
+	void incoming(message_vector &messages, const message_callback &send) override;
+	bool requestKeyframe(const message_callback &send) override;
+	bool requestBitrate(unsigned int bitrate, const message_callback &send) override;
 
 
-	bool requestKeyframe() override;
+	// For backward compatibility
+	[[deprecated("Use Track::requestKeyframe()")]] inline bool requestKeyframe() { return false; };
+	[[deprecated("Use Track::requestBitrate()")]] inline void requestBitrate(unsigned int) {};
 
 
 protected:
 protected:
-	void pushREMB(unsigned int bitrate);
-	void pushRR(unsigned int lastSR_delay);
-
-	void pushPLI();
+	void pushREMB(const message_callback &send, unsigned int bitrate);
+	void pushRR(const message_callback &send,unsigned int lastSrDelay);
+	void pushPLI(const message_callback &send);
 
 
-	unsigned int mRequestedBitrate = 0;
 	SSRC mSsrc = 0;
 	SSRC mSsrc = 0;
 	uint32_t mGreatestSeqNo = 0;
 	uint32_t mGreatestSeqNo = 0;
 	uint64_t mSyncRTPTS, mSyncNTPTS;
 	uint64_t mSyncRTPTS, mSyncNTPTS;
+
+	std::atomic<unsigned int> mRequestedBitrate = 0;
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 14 - 15
include/rtc/rtcpsrreporter.hpp

@@ -6,34 +6,33 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  */
  */
 
 
-#ifndef RTC_RTCP_SENDER_REPORTABLE_H
-#define RTC_RTCP_SENDER_REPORTABLE_H
+#ifndef RTC_RTCP_SR_REPORTER_H
+#define RTC_RTCP_SR_REPORTER_H
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
-#include "mediahandlerelement.hpp"
-#include "message.hpp"
+#include "mediahandler.hpp"
 #include "rtppacketizationconfig.hpp"
 #include "rtppacketizationconfig.hpp"
+#include "rtp.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandlerElement {
-	void addToReport(RtpHeader *rtp, uint32_t rtpSize);
-	message_ptr getSenderReport(uint32_t timestamp);
-
+class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandler {
 public:
 public:
-	/// RTP configuration
-	const shared_ptr<RtpPacketizationConfig> rtpConfig;
-
 	RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig);
 	RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig);
 
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
-
 	uint32_t lastReportedTimestamp() const;
 	uint32_t lastReportedTimestamp() const;
 	void setNeedsToReport();
 	void setNeedsToReport();
 
 
+	void outgoing(message_vector &messages, const message_callback &send) override;
+
+	// TODO: remove this
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
+
 private:
 private:
+	void addToReport(RtpHeader *rtp, uint32_t rtpSize);
+	message_ptr getSenderReport(uint32_t timestamp);
+
 	uint32_t mPacketCount = 0;
 	uint32_t mPacketCount = 0;
 	uint32_t mPayloadOctets = 0;
 	uint32_t mPayloadOctets = 0;
 	uint32_t mLastReportedTimestamp = 0;
 	uint32_t mLastReportedTimestamp = 0;
@@ -44,4 +43,4 @@ private:
 
 
 #endif /* RTC_ENABLE_MEDIA */
 #endif /* RTC_ENABLE_MEDIA */
 
 
-#endif /* RTC_RTCP_SENDER_REPORTABLE_H */
+#endif /* RTC_RTCP_SR_REPORTER_H */

+ 22 - 20
include/rtc/rtp.hpp

@@ -39,6 +39,7 @@ struct RTC_CPP_EXPORT RtpExtensionHeader {
 
 
 	void clearBody();
 	void clearBody();
 	void writeCurrentVideoOrientation(size_t offset, uint8_t id, uint8_t value);
 	void writeCurrentVideoOrientation(size_t offset, uint8_t id, uint8_t value);
+	void writeOneByteHeader(size_t offset, uint8_t id, const byte *value, size_t size);
 };
 };
 
 
 struct RTC_CPP_EXPORT RtpHeader {
 struct RTC_CPP_EXPORT RtpHeader {
@@ -88,19 +89,20 @@ struct RTC_CPP_EXPORT RtcpReportBlock {
 
 
 	[[nodiscard]] uint16_t seqNoCycles() const;
 	[[nodiscard]] uint16_t seqNoCycles() const;
 	[[nodiscard]] uint16_t highestSeqNo() const;
 	[[nodiscard]] uint16_t highestSeqNo() const;
+	[[nodiscard]] uint32_t extendedHighestSeqNo() const;
 	[[nodiscard]] uint32_t jitter() const;
 	[[nodiscard]] uint32_t jitter() const;
 	[[nodiscard]] uint32_t delaySinceSR() const;
 	[[nodiscard]] uint32_t delaySinceSR() const;
 
 
 	[[nodiscard]] SSRC getSSRC() const;
 	[[nodiscard]] SSRC getSSRC() const;
 	[[nodiscard]] uint32_t getNTPOfSR() const;
 	[[nodiscard]] uint32_t getNTPOfSR() const;
-	[[nodiscard]] unsigned int getLossPercentage() const;
-	[[nodiscard]] unsigned int getPacketLostCount() const;
+	[[nodiscard]] uint8_t getFractionLost() const;
+	[[nodiscard]] unsigned int getPacketsLostCount() const;
 
 
 	void preparePacket(SSRC in_ssrc, unsigned int packetsLost, unsigned int totalPackets,
 	void preparePacket(SSRC in_ssrc, unsigned int packetsLost, unsigned int totalPackets,
 	                   uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter,
 	                   uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter,
 	                   uint64_t lastSR_NTP, uint64_t lastSR_DELAY);
 	                   uint64_t lastSR_NTP, uint64_t lastSR_DELAY);
 	void setSSRC(SSRC in_ssrc);
 	void setSSRC(SSRC in_ssrc);
-	void setPacketsLost(unsigned int packetsLost, unsigned int totalPackets);
+	void setPacketsLost(uint8_t fractionLost, unsigned int packetsLostCount);
 	void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles);
 	void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles);
 	void setJitter(uint32_t jitter);
 	void setJitter(uint32_t jitter);
 	void setNTPOfSR(uint64_t ntp);
 	void setNTPOfSR(uint64_t ntp);
@@ -353,23 +355,23 @@ struct RTC_CPP_EXPORT RtpRtx {
 };
 };
 
 
 // For backward compatibility, do not use
 // For backward compatibility, do not use
-using RTP_ExtensionHeader = RtpExtensionHeader;
-using RTP = RtpHeader;
-using RTCP_ReportBlock = RtcpReportBlock;
-using RTCP_HEADER = RtcpHeader;
-using RTCP_FB_HEADER = RtcpFbHeader;
-using RTCP_SR = RtcpSr;
-using RTCP_SDES_ITEM = RtcpSdesItem;
-using RTCP_SDES_CHUNK = RtcpSdesChunk;
-using RTCP_SDES = RtcpSdes;
-using RTCP_RR = RtcpRr;
-using RTCP_REMB = RtcpRemb;
-using RTCP_PLI = RtcpPli;
-using RTCP_FIR_PART = RtcpFirPart;
-using RTCP_FIR = RtcpFir;
-using RTCP_NACK_PART = RtcpNackPart;
-using RTCP_NACK = RtcpNack;
-using RTP_RTX = RtpRtx;
+using RTP_ExtensionHeader [[deprecated]] = RtpExtensionHeader;
+using RTP [[deprecated]] = RtpHeader;
+using RTCP_ReportBlock [[deprecated]] = RtcpReportBlock;
+using RTCP_HEADER [[deprecated]] = RtcpHeader;
+using RTCP_FB_HEADER [[deprecated]] = RtcpFbHeader;
+using RTCP_SR [[deprecated]] = RtcpSr;
+using RTCP_SDES_ITEM [[deprecated]] = RtcpSdesItem;
+using RTCP_SDES_CHUNK [[deprecated]] = RtcpSdesChunk;
+using RTCP_SDES [[deprecated]] = RtcpSdes;
+using RTCP_RR [[deprecated]] = RtcpRr;
+using RTCP_REMB [[deprecated]] = RtcpRemb;
+using RTCP_PLI [[deprecated]] = RtcpPli;
+using RTCP_FIR_PART [[deprecated]] = RtcpFirPart;
+using RTCP_FIR [[deprecated]] = RtcpFir;
+using RTCP_NACK_PART [[deprecated]] = RtcpNackPart;
+using RTCP_NACK [[deprecated]] = RtcpNack;
+using RTP_RTX [[deprecated]] = RtpRtx;
 
 
 #pragma pack(pop)
 #pragma pack(pop)
 
 

+ 34 - 0
include/rtc/rtpdepacketizer.hpp

@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2024 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_RTP_DEPACKETIZER_H
+#define RTC_RTP_DEPACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandler.hpp"
+#include "message.hpp"
+
+namespace rtc {
+
+class RTC_CPP_EXPORT RtpDepacketizer : public MediaHandler {
+public:
+	RtpDepacketizer() = default;
+	virtual ~RtpDepacketizer() = default;
+
+	virtual void incoming(message_vector &messages, const message_callback &send) override;
+};
+
+using OpusRtpDepacketizer = RtpDepacketizer;
+using AACRtpDepacketizer = RtpDepacketizer;
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_RTP_DEPACKETIZER_H */

+ 13 - 5
include/rtc/rtppacketizationconfig.hpp

@@ -18,11 +18,11 @@ namespace rtc {
 // RTP configuration used in packetization process
 // RTP configuration used in packetization process
 class RTC_CPP_EXPORT RtpPacketizationConfig {
 class RTC_CPP_EXPORT RtpPacketizationConfig {
 public:
 public:
-	const SSRC ssrc;
-	const std::string cname;
-	const uint8_t payloadType;
-	const uint32_t clockRate;
-	const uint8_t videoOrientationId;
+	SSRC ssrc;
+	std::string cname;
+	uint8_t payloadType;
+	uint32_t clockRate;
+	uint8_t videoOrientationId;
 
 
 	// current sequence number
 	// current sequence number
 	uint16_t sequenceNumber;
 	uint16_t sequenceNumber;
@@ -53,6 +53,14 @@ public:
 	///   3 - 270 degrees
 	///   3 - 270 degrees
 	uint8_t videoOrientation = 0;
 	uint8_t videoOrientation = 0;
 
 
+	// MID Extension Header
+	uint8_t midId = 0;
+	optional<std::string> mid;
+
+	// RID Extension Header
+	uint8_t ridId = 0;
+	optional<std::string> rid;
+
 	/// Construct RTP configuration used in packetization process
 	/// Construct RTP configuration used in packetization process
 	/// @param ssrc SSRC of source
 	/// @param ssrc SSRC of source
 	/// @param cname CNAME of source
 	/// @param cname CNAME of source

+ 54 - 11
include/rtc/rtppacketizer.hpp

@@ -11,33 +11,76 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
+#include "mediahandler.hpp"
 #include "message.hpp"
 #include "message.hpp"
 #include "rtppacketizationconfig.hpp"
 #include "rtppacketizationconfig.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-/// Class responsible for RTP packetization
-class RTC_CPP_EXPORT RtpPacketizer {
-	static const auto rtpHeaderSize = 12;
-	static const auto rtpExtHeaderCvoSize = 8;
-
+/// RTP packetizer
+class RTC_CPP_EXPORT RtpPacketizer : public MediaHandler {
 public:
 public:
-	// RTP configuration
-	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(shared_ptr<RtpPacketizationConfig> rtpConfig);
 	RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig);
+	virtual ~RtpPacketizer();
 
 
-	/// Creates RTP packet for given payload based on `rtpConfig`.
+	virtual void media(const Description::Media &desc) override;
+	virtual void outgoing(message_vector &messages, const message_callback &send) override;
+
+	/// RTP packetization config
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
+
+protected:
+	/// Creates RTP packet for given payload
 	/// @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 shared_ptr<binary> packetize(shared_ptr<binary> payload, bool setMark);
+	virtual message_ptr packetize(shared_ptr<binary> payload, bool mark);
+
+private:
+	static const auto RtpHeaderSize = 12;
+	static const auto RtpExtHeaderCvoSize = 8;
+};
+
+// Generic audio RTP packetizer
+template <uint32_t DEFAULT_CLOCK_RATE>
+class RTC_CPP_EXPORT AudioRtpPacketizer final : public RtpPacketizer {
+public:
+	inline static const uint32_t DefaultClockRate = DEFAULT_CLOCK_RATE;
+	inline static const uint32_t defaultClockRate [[deprecated("Use DefaultClockRate")]] =
+	    DEFAULT_CLOCK_RATE; // for backward compatibility
+
+	AudioRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)
+	    : RtpPacketizer(std::move(rtpConfig)) {}
 };
 };
 
 
+// Audio RTP packetizers
+using OpusRtpPacketizer = AudioRtpPacketizer<48000>;
+using AACRtpPacketizer = AudioRtpPacketizer<48000>;
+
+// Dummy wrapper for backward compatibility, do not use
+class RTC_CPP_EXPORT PacketizationHandler final : public MediaHandler {
+public:
+	PacketizationHandler(shared_ptr<RtpPacketizer> packetizer)
+	    : mPacketizer(std::move(packetizer)) {}
+
+	inline void outgoing(message_vector &messages, const message_callback &send) {
+		return mPacketizer->outgoing(messages, send);
+	}
+
+private:
+	shared_ptr<RtpPacketizer> mPacketizer;
+};
+
+// Audio packetization handlers for backward compatibility, do not use
+using OpusPacketizationHandler [[deprecated("Add OpusRtpPacketizer directly")]] =
+    PacketizationHandler;
+using AACPacketizationHandler [[deprecated("Add AACRtpPacketizer directly")]] =
+    PacketizationHandler;
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif /* RTC_ENABLE_MEDIA */
 #endif /* RTC_ENABLE_MEDIA */

+ 4 - 0
include/rtc/track.hpp

@@ -41,9 +41,13 @@ public:
 	bool isClosed(void) const override;
 	bool isClosed(void) const override;
 	size_t maxMessageSize() const override;
 	size_t maxMessageSize() const override;
 
 
+	void onFrame(std::function<void(binary data, FrameInfo frame)> callback);
+
 	bool requestKeyframe();
 	bool requestKeyframe();
+	bool requestBitrate(unsigned int bitrate);
 
 
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
+	void chainMediaHandler(shared_ptr<MediaHandler> handler);
 	shared_ptr<MediaHandler> getMediaHandler();
 	shared_ptr<MediaHandler> getMediaHandler();
 
 
 	// Deprecated, use setMediaHandler() and getMediaHandler()
 	// Deprecated, use setMediaHandler() and getMediaHandler()

+ 9 - 0
include/rtc/version.h

@@ -0,0 +1,9 @@
+#ifndef RTC_VERSION_H
+#define RTC_VERSION_H
+
+#define RTC_VERSION_MAJOR 0
+#define RTC_VERSION_MINOR 20
+#define RTC_VERSION_PATCH 1
+#define RTC_VERSION "0.20.1"
+
+#endif

+ 4 - 9
include/rtc/websocket.hpp

@@ -13,7 +13,7 @@
 
 
 #include "channel.hpp"
 #include "channel.hpp"
 #include "common.hpp"
 #include "common.hpp"
-#include "configuration.hpp" // for ProxyServer
+#include "configuration.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -32,14 +32,7 @@ public:
 		Closed = 3,
 		Closed = 3,
 	};
 	};
 
 
-	struct Configuration {
-		bool disableTlsVerification = false; // if true, don't verify the TLS certificate
-		optional<ProxyServer> proxyServer;   // only non-authenticated http supported for now
-		std::vector<string> protocols;
-		optional<std::chrono::milliseconds> connectionTimeout; // zero to disable
-		optional<std::chrono::milliseconds> pingInterval; // zero to disable
-		optional<int> maxOutstandingPings;
-	};
+	using Configuration = WebSocketConfiguration;
 
 
 	WebSocket();
 	WebSocket();
 	WebSocket(Configuration config);
 	WebSocket(Configuration config);
@@ -65,6 +58,8 @@ private:
 	using CheshireCat<impl::WebSocket>::impl;
 	using CheshireCat<impl::WebSocket>::impl;
 };
 };
 
 
+std::ostream &operator<<(std::ostream &out, WebSocket::State state);
+
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif

+ 2 - 9
include/rtc/websocketserver.hpp

@@ -12,6 +12,7 @@
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
 #include "common.hpp"
 #include "common.hpp"
+#include "configuration.hpp"
 #include "websocket.hpp"
 #include "websocket.hpp"
 
 
 namespace rtc {
 namespace rtc {
@@ -24,15 +25,7 @@ struct WebSocketServer;
 
 
 class RTC_CPP_EXPORT WebSocketServer final : private CheshireCat<impl::WebSocketServer> {
 class RTC_CPP_EXPORT WebSocketServer final : private CheshireCat<impl::WebSocketServer> {
 public:
 public:
-	struct Configuration {
-		uint16_t port = 8080;
-		bool enableTls = false;
-		optional<string> certificatePemFile;
-		optional<string> keyPemFile;
-		optional<string> keyPemPass;
-		optional<string> bindAddress;
-		optional<std::chrono::milliseconds> connectionTimeout;
-	};
+	using Configuration = WebSocketServerConfiguration;
 
 
 	WebSocketServer();
 	WebSocketServer();
 	WebSocketServer(Configuration config);
 	WebSocketServer(Configuration config);

+ 3 - 4
pages/Makefile

@@ -9,7 +9,7 @@ CONFFILE=$(BASEDIR)/pelicanconf.py
 PUBLISHCONF=$(BASEDIR)/publishconf.py
 PUBLISHCONF=$(BASEDIR)/publishconf.py
 
 
 GITHUB_PAGES_BRANCH=gh-pages
 GITHUB_PAGES_BRANCH=gh-pages
-
+GITHUB_PAGES_CNAME=libdatachannel.org
 
 
 DEBUG ?= 0
 DEBUG ?= 0
 ifeq ($(DEBUG), 1)
 ifeq ($(DEBUG), 1)
@@ -72,8 +72,7 @@ publish:
 	"$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS)
 	"$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS)
 
 
 github: publish
 github: publish
-	ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) "$(OUTPUTDIR)"
-	git push origin $(GITHUB_PAGES_BRANCH)
+	ghp-import -m "Generate Pelican site" -po -b $(GITHUB_PAGES_BRANCH) -c $(GITHUB_PAGES_CNAME) "$(OUTPUTDIR)"
 
 
 
 
-.PHONY: html help clean regenerate serve serve-global devserver publish github
+.PHONY: html help clean regenerate serve serve-global devserver publish github

+ 0 - 1
pages/content/extra/CNAME

@@ -1 +0,0 @@
-libdatachannel.org

+ 1 - 1
pages/content/pages/index.md

@@ -34,7 +34,7 @@ save_as: index.html
 		<h3>Portable</h3>
 		<h3>Portable</h3>
 		<ul>
 		<ul>
 			<li>Support for POSIX platforms (including GNU/Linux, Android, FreeBSD, Apple macOS and iOS) and Microsoft Windows</li>
 			<li>Support for POSIX platforms (including GNU/Linux, Android, FreeBSD, Apple macOS and iOS) and Microsoft Windows</li>
-			<li>Support for both <a href="https://www.openssl.org/">OpenSSL</a> and <a href="https://www.gnutls.org/">GnuTLS</a> as TLS backend
+			<li>Support for <a href="https://www.gnutls.org/">GnuTLS</a>, <a href="https://www.trustedfirmware.org/projects/mbed-tls/">Mbed TLS</a>, or <a href="https://www.openssl.org/">OpenSSL</a> as TLS backend
 			<li>Code using Data Channels and WebSockets may be compiled as-is to WebAssembly for browsers with <a href="https://github.com/paullouisageneau/datachannel-wasm">datachannel-wasm</a></li>
 			<li>Code using Data Channels and WebSockets may be compiled as-is to WebAssembly for browsers with <a href="https://github.com/paullouisageneau/datachannel-wasm">datachannel-wasm</a></li>
 		</ul>
 		</ul>
 	</section>
 	</section>

+ 30 - 2
pages/content/pages/reference.md

@@ -386,6 +386,20 @@ Arguments:
 
 
 Return value: the maximum stream ID (`stream` for a Data Channel may be set from 0 to this value included) or a negative error code
 Return value: the maximum stream ID (`stream` for a Data Channel may be set from 0 to this value included) or a negative error code
 
 
+#### rtcGetRemoteMaxMessageSize
+
+```
+int rtcGetRemoteMaxMessageSize(int pc)
+```
+
+Retrieves the maximum message size for data channels on the peer connection as negotiated with the remote peer.
+
+Arguments:
+
+- `pc`: the Peer Connection identifier
+
+Return value: the maximum message size for data channels or a negative error code
+
 ### Channel (Common API for Data Channel, Track, and WebSocket)
 ### Channel (Common API for 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.
 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.
@@ -522,6 +536,20 @@ Arguments:
 
 
 Return value: `true` if the channel exists and is closed (not open and not connecting), `false` otherwise
 Return value: `true` if the channel exists and is closed (not open and not connecting), `false` otherwise
 
 
+#### rtcGetMaxMessageSize
+
+```
+int rtcGetMaxMessageSize(int id)
+```
+
+Retrieves the maximum message size for the channel.
+
+Arguments:
+
+- `id`: the channel identifier
+
+Return value: the maximum message size or a negative error code
+
 #### rtcGetBufferedAmount
 #### rtcGetBufferedAmount
 
 
 ```
 ```
@@ -619,8 +647,8 @@ Arguments:
   - `reliability`: a structure of reliability settings containing:
   - `reliability`: a structure of reliability settings containing:
     - `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
     - `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
     - `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
     - `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
-    - `maxPacketLifeTime`: if unreliable, maximum packet life time in milliseconds
-    - `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of retransmissions (0 means no retransmission)
+    - `maxPacketLifeTime`: if unreliable, time window in milliseconds during which transmissions and retransmissions may occur
+    - `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of attempted retransmissions (0 means no retransmission)
   - `protocol` (optional): a user-defined UTF-8 string representing the Data Channel protocol, empty if NULL
   - `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
   - `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
   - `manualStream`: if `true`, the Data Channel will use `stream` as stream ID, else an available id is automatically selected

+ 7 - 2
pages/pelicanconf.py

@@ -7,8 +7,7 @@ SITENAME = 'libdatachannel'
 SITEURL = ''
 SITEURL = ''
 
 
 PATH = 'content'
 PATH = 'content'
-STATIC_PATHS = ['images', 'extra/CNAME']
-EXTRA_PATH_METADATA = {'extra/CNAME': {'path': 'CNAME'}}
+STATIC_PATHS = ['images']
 
 
 THEME = 'theme'
 THEME = 'theme'
 
 
@@ -41,5 +40,11 @@ DEFAULT_PAGINATION = False
 #RELATIVE_URLS = True
 #RELATIVE_URLS = True
 
 
 MARKDOWN = {
 MARKDOWN = {
+    'extension_configs': {
+        'markdown.extensions.codehilite': {'css_class': 'highlight'},
+        'markdown.extensions.extra': {},
+        'markdown.extensions.meta': {},
+        'markdown.extensions.fenced_code': {},
+    },
     'tab_length': 2,
     'tab_length': 2,
 }
 }

+ 225 - 0
src/av1rtppacketizer.cpp

@@ -0,0 +1,225 @@
+/**
+ * Copyright (c) 2023 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#if RTC_ENABLE_MEDIA
+
+#include "av1rtppacketizer.hpp"
+
+#include "impl/internals.hpp"
+
+namespace rtc {
+
+const auto payloadHeaderSize = 1;
+
+const auto zMask = byte(0b10000000);
+const auto yMask = byte(0b01000000);
+const auto nMask = byte(0b00001000);
+
+const auto wBitshift = 4;
+
+const auto obuFrameTypeMask = byte(0b01111000);
+const auto obuFrameTypeBitshift = 3;
+
+const auto obuHeaderSize = 1;
+const auto obuHasExtensionMask = byte(0b00000100);
+const auto obuHasSizeMask = byte(0b00000010);
+
+const auto obuFrameTypeSequenceHeader = byte(1);
+
+const auto obuTemporalUnitDelimiter = std::vector<byte>{byte(0x12), byte(0x00)};
+
+const auto oneByteLeb128Size = 1;
+
+const uint8_t sevenLsbBitmask = 0b01111111;
+const uint8_t msbBitmask = 0b10000000;
+
+std::vector<binary_ptr> extractTemporalUnitObus(binary_ptr message) {
+	std::vector<shared_ptr<binary>> obus{};
+
+	if (message->size() <= 2 || (message->at(0) != obuTemporalUnitDelimiter.at(0)) ||
+	    (message->at(1) != obuTemporalUnitDelimiter.at(1))) {
+		return obus;
+	}
+
+	size_t messageIndex = 2;
+	while (messageIndex < message->size()) {
+		if ((message->at(messageIndex) & obuHasSizeMask) == byte(0)) {
+			return obus;
+		}
+
+		if ((message->at(messageIndex) & obuHasExtensionMask) != byte(0)) {
+			messageIndex++;
+		}
+
+		// https://aomediacodec.github.io/av1-spec/#leb128
+		uint32_t obuLength = 0;
+		uint8_t leb128Size = 0;
+		while (leb128Size < 8) {
+			auto leb128Index = messageIndex + leb128Size + obuHeaderSize;
+			if (message->size() < leb128Index) {
+				break;
+			}
+
+			auto leb128_byte = uint8_t(message->at(leb128Index));
+
+			obuLength |= ((leb128_byte & sevenLsbBitmask) << (leb128Size * 7));
+			leb128Size++;
+
+			if (!(leb128_byte & msbBitmask)) {
+				break;
+			}
+		}
+
+		obus.push_back(std::make_shared<binary>(message->begin() + messageIndex,
+		                                        message->begin() + messageIndex + obuHeaderSize +
+		                                            leb128Size + obuLength));
+
+		messageIndex += obuHeaderSize + leb128Size + obuLength;
+	}
+
+	return obus;
+}
+
+/*
+ *  0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |Z|Y| W |N|-|-|-|
+ * +-+-+-+-+-+-+-+-+
+ *
+ *	Z: MUST be set to 1 if the first OBU element is an
+ *	   OBU fragment that is a continuation of an OBU fragment
+ *	   from the previous packet, and MUST be set to 0 otherwise.
+ *
+ *	Y: MUST be set to 1 if the last OBU element is an OBU fragment
+ *	   that will continue in the next packet, and MUST be set to 0 otherwise.
+ *
+ *	W: two bit field that describes the number of OBU elements in the packet.
+ *	   This field MUST be set equal to 0 or equal to the number of OBU elements
+ *	   contained in the packet. If set to 0, each OBU element MUST be preceded by
+ *	   a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element
+ *	   MUST NOT be preceded by a length field. Instead, the length of the last OBU
+ *	   element contained in the packet can be calculated as follows:
+ *	Length of the last OBU element =
+ *	   length of the RTP payload
+ *	 - length of aggregation header
+ *	 - length of previous OBU elements including length fields
+ *
+ *	N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set
+ *     to 0 otherwise.
+ *
+ * https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
+ *
+ **/
+
+std::vector<binary_ptr> AV1RtpPacketizer::packetizeObu(binary_ptr message,
+                                                       uint16_t maxFragmentSize) {
+
+	std::vector<shared_ptr<binary>> payloads{};
+	size_t messageIndex = 0;
+
+	if (message->size() < 1) {
+		return payloads;
+	}
+
+	// Cache sequence header and packetize with next OBU
+	auto frameType = (message->at(0) & obuFrameTypeMask) >> obuFrameTypeBitshift;
+	if (frameType == obuFrameTypeSequenceHeader) {
+		sequenceHeader = std::make_shared<binary>(message->begin(), message->end());
+		return payloads;
+	}
+
+	size_t messageRemaining = message->size();
+	while (messageRemaining > 0) {
+		auto obuCount = 1;
+		auto metadataSize = payloadHeaderSize;
+
+		if (sequenceHeader != nullptr) {
+			obuCount++;
+			metadataSize += /* 1 byte leb128 */ 1 + int(sequenceHeader->size());
+		}
+
+		auto payload = std::make_shared<binary>(
+		    std::min(size_t(maxFragmentSize), messageRemaining + metadataSize));
+		auto payloadOffset = payloadHeaderSize;
+
+		payload->at(0) = byte(obuCount) << wBitshift;
+
+		// Packetize cached SequenceHeader
+		if (obuCount == 2) {
+			payload->at(0) ^= nMask;
+			payload->at(1) = byte(sequenceHeader->size() & sevenLsbBitmask);
+			payloadOffset += oneByteLeb128Size;
+
+			std::memcpy(payload->data() + payloadOffset, sequenceHeader->data(),
+			            sequenceHeader->size());
+			payloadOffset += int(sequenceHeader->size());
+
+			sequenceHeader = nullptr;
+		}
+
+		// Copy as much of OBU as possible into Payload
+		auto payloadRemaining = payload->size() - payloadOffset;
+		std::memcpy(payload->data() + payloadOffset, message->data() + messageIndex,
+		            payloadRemaining);
+		messageRemaining -= payloadRemaining;
+		messageIndex += payloadRemaining;
+
+		// Does this Fragment contain an OBU that started in a previous payload
+		if (payloads.size() > 0) {
+			payload->at(0) ^= zMask;
+		}
+
+		// This OBU will be continued in next Payload
+		if (messageIndex < message->size()) {
+			payload->at(0) ^= yMask;
+		}
+
+		payloads.push_back(payload);
+	}
+
+	return payloads;
+}
+
+AV1RtpPacketizer::AV1RtpPacketizer(AV1RtpPacketizer::Packetization packetization,
+                                   shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                   uint16_t maxFragmentSize)
+    : RtpPacketizer(rtpConfig), maxFragmentSize(maxFragmentSize),
+      packetization(packetization) {}
+
+void AV1RtpPacketizer::outgoing(message_vector &messages,
+                                [[maybe_unused]] const message_callback &send) {
+	message_vector result;
+	for (const auto &message : messages) {
+		std::vector<binary_ptr> obus;
+		if (packetization == AV1RtpPacketizer::Packetization::TemporalUnit) {
+			obus = extractTemporalUnitObus(message);
+		} else {
+			obus.push_back(message);
+		}
+
+		std::vector<binary_ptr> fragments;
+		for (auto obu : obus) {
+			auto p = packetizeObu(obu, maxFragmentSize);
+			fragments.insert(fragments.end(), p.begin(), p.end());
+		}
+
+		if (fragments.size() == 0)
+			continue;
+
+		for (size_t i = 0; i < fragments.size() - 1; i++)
+			result.push_back(packetize(fragments[i], false));
+
+		result.push_back(packetize(fragments[fragments.size() - 1], true));
+	}
+
+	messages.swap(result);
+}
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */

+ 15 - 15
src/candidate.cpp

@@ -248,40 +248,40 @@ optional<uint16_t> Candidate::port() const {
 	return isResolved() ? std::make_optional(mPort) : nullopt;
 	return isResolved() ? std::make_optional(mPort) : nullopt;
 }
 }
 
 
-} // namespace rtc
-
-std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate) {
-	return out << std::string(candidate);
+std::ostream &operator<<(std::ostream &out, const Candidate &candidate) {
+	return out << string(candidate);
 }
 }
 
 
-std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type) {
+std::ostream &operator<<(std::ostream &out, const Candidate::Type &type) {
 	switch (type) {
 	switch (type) {
-	case rtc::Candidate::Type::Host:
+	case Candidate::Type::Host:
 		return out << "host";
 		return out << "host";
-	case rtc::Candidate::Type::PeerReflexive:
+	case Candidate::Type::PeerReflexive:
 		return out << "prflx";
 		return out << "prflx";
-	case rtc::Candidate::Type::ServerReflexive:
+	case Candidate::Type::ServerReflexive:
 		return out << "srflx";
 		return out << "srflx";
-	case rtc::Candidate::Type::Relayed:
+	case Candidate::Type::Relayed:
 		return out << "relay";
 		return out << "relay";
 	default:
 	default:
 		return out << "unknown";
 		return out << "unknown";
 	}
 	}
 }
 }
 
 
-std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType) {
+std::ostream &operator<<(std::ostream &out, const Candidate::TransportType &transportType) {
 	switch (transportType) {
 	switch (transportType) {
-	case rtc::Candidate::TransportType::Udp:
+	case Candidate::TransportType::Udp:
 		return out << "UDP";
 		return out << "UDP";
-	case rtc::Candidate::TransportType::TcpActive:
+	case Candidate::TransportType::TcpActive:
 		return out << "TCP_active";
 		return out << "TCP_active";
-	case rtc::Candidate::TransportType::TcpPassive:
+	case Candidate::TransportType::TcpPassive:
 		return out << "TCP_passive";
 		return out << "TCP_passive";
-	case rtc::Candidate::TransportType::TcpSo:
+	case Candidate::TransportType::TcpSo:
 		return out << "TCP_so";
 		return out << "TCP_so";
-	case rtc::Candidate::TransportType::TcpUnknown:
+	case Candidate::TransportType::TcpUnknown:
 		return out << "TCP_unknown";
 		return out << "TCP_unknown";
 	default:
 	default:
 		return out << "unknown";
 		return out << "unknown";
 	}
 	}
 }
 }
+
+} // namespace rtc

+ 207 - 97
src/capi.cpp

@@ -29,7 +29,6 @@ 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<MediaChainableHandler>> rtcpChainableHandlerMap;
 std::unordered_map<int, shared_ptr<RtcpSrReporter>> rtcpSrReporterMap;
 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
@@ -120,7 +119,6 @@ void eraseTrack(int tr) {
 		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
 	rtcpSrReporterMap.erase(tr);
 	rtcpSrReporterMap.erase(tr);
-	rtcpChainableHandlerMap.erase(tr);
 	rtpConfigMap.erase(tr);
 	rtpConfigMap.erase(tr);
 #endif
 #endif
 	userPointerMap.erase(tr);
 	userPointerMap.erase(tr);
@@ -133,8 +131,7 @@ size_t eraseAll() {
 	trackMap.clear();
 	trackMap.clear();
 	peerConnectionMap.clear();
 	peerConnectionMap.clear();
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
-	count += rtcpChainableHandlerMap.size() + rtcpSrReporterMap.size() + rtpConfigMap.size();
-	rtcpChainableHandlerMap.clear();
+	count += rtcpSrReporterMap.size() + rtpConfigMap.size();
 	rtcpSrReporterMap.clear();
 	rtcpSrReporterMap.clear();
 	rtpConfigMap.clear();
 	rtpConfigMap.clear();
 #endif
 #endif
@@ -170,7 +167,6 @@ void eraseChannel(int id) {
 		userPointerMap.erase(id);
 		userPointerMap.erase(id);
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 		rtcpSrReporterMap.erase(id);
 		rtcpSrReporterMap.erase(id);
-		rtcpChainableHandlerMap.erase(id);
 		rtpConfigMap.erase(id);
 		rtpConfigMap.erase(id);
 #endif
 #endif
 		return;
 		return;
@@ -253,20 +249,6 @@ void emplaceRtcpSrReporter(shared_ptr<RtcpSrReporter> ptr, int tr) {
 	rtcpSrReporterMap.emplace(std::make_pair(tr, ptr));
 	rtcpSrReporterMap.emplace(std::make_pair(tr, ptr));
 }
 }
 
 
-shared_ptr<MediaChainableHandler> getMediaChainableHandler(int id) {
-	std::lock_guard lock(mutex);
-	if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) {
-		return it->second;
-	} else {
-		throw std::invalid_argument("RTCP chainable handler ID does not exist");
-	}
-}
-
-void emplaceMediaChainableHandler(shared_ptr<MediaChainableHandler> ptr, int tr) {
-	std::lock_guard lock(mutex);
-	rtcpChainableHandlerMap.emplace(std::make_pair(tr, ptr));
-}
-
 shared_ptr<RtpPacketizationConfig> getRtpConfig(int id) {
 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()) {
@@ -303,31 +285,30 @@ public:
 	MediaInterceptor(MessageCallback cb) : incomingCallback(cb) {}
 	MediaInterceptor(MessageCallback cb) : incomingCallback(cb) {}
 
 
 	// Called when there is traffic coming from the peer
 	// Called when there is traffic coming from the peer
-	message_ptr incoming(message_ptr msg) override {
+	void incoming(message_vector &messages,
+	              [[maybe_unused]] const message_callback &send) override {
 		// If no callback is provided, just forward the message on
 		// If no callback is provided, just forward the message on
-		if (!incomingCallback) {
-			return msg;
-		}
+		if (!incomingCallback)
+			return;
 
 
-		auto res = incomingCallback(reinterpret_cast<void *>(msg->data()), int(msg->size()));
+		message_vector result;
+		for (auto &msg : messages) {
+			auto res = incomingCallback(reinterpret_cast<void *>(msg->data()), int(msg->size()));
 
 
-		// If a null pointer was returned, drop the incoming message
-		if (res == nullptr) {
-			return nullptr;
-		}
+			// If a null pointer was returned, drop the incoming message
+			if (!res)
+				continue;
 
 
-		// If the original data pointer was returned, forward the incoming message
-		if (res == msg->data()) {
-			return msg;
+			if (res == msg->data()) {
+				// If the original data pointer was returned, forward the incoming message
+				result.push_back(std::move(msg));
+			} else {
+				// else construct a true message_ptr from the returned opaque pointer
+				result.push_back(
+				    make_message_from_opaque_ptr(std::move(reinterpret_cast<rtcMessage *>(res))));
+			}
 		}
 		}
-
-		// Construct a true message_ptr from the returned opaque pointer
-		return make_message_from_opaque_ptr(std::move(reinterpret_cast<rtcMessage *>(res)));
-	};
-
-	// Called when there is traffic that needs to be sent to the peer
-	// This is a no-op for media interceptors
-	message_ptr outgoing(message_ptr ptr) override { return ptr; };
+	}
 
 
 private:
 private:
 	MessageCallback incomingCallback;
 	MessageCallback incomingCallback;
@@ -494,6 +475,20 @@ int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
 	});
 	});
 }
 }
 
 
+int rtcSetIceStateChangeCallback(int pc, rtcIceStateChangeCallbackFunc cb) {
+	return wrap([&] {
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onIceStateChange([pc, cb](PeerConnection::IceState state) {
+				if (auto ptr = getUserPointer(pc))
+					cb(pc, static_cast<rtcIceState>(state), *ptr);
+			});
+		else
+			peerConnection->onIceStateChange(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);
@@ -683,6 +678,13 @@ int rtcGetMaxDataChannelStream(int pc) {
 	});
 	});
 }
 }
 
 
+int rtcGetRemoteMaxMessageSize(int pc) {
+	return wrap([&] {
+		auto peerConnection = getPeerConnection(pc);
+		return int(peerConnection->remoteMaxMessageSize());
+	});
+}
+
 int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
 int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
 	return wrap([&] {
 	return wrap([&] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
@@ -786,6 +788,13 @@ bool rtcIsClosed(int id) {
 	return wrap([id] { return getChannel(id)->isClosed() ? 0 : 1; }) == 0 ? true : false;
 	return wrap([id] { return getChannel(id)->isClosed() ? 0 : 1; }) == 0 ? true : false;
 }
 }
 
 
+int rtcMaxMessageSize(int id) {
+	return wrap([id] {
+		auto channel = getChannel(id);
+		return int(channel->maxMessageSize());
+	});
+}
+
 int rtcGetBufferedAmount(int id) {
 int rtcGetBufferedAmount(int id) {
 	return wrap([id] {
 	return wrap([id] {
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
@@ -892,15 +901,10 @@ int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *
 			auto *reliability = &init->reliability;
 			auto *reliability = &init->reliability;
 			dci.reliability.unordered = reliability->unordered;
 			dci.reliability.unordered = reliability->unordered;
 			if (reliability->unreliable) {
 			if (reliability->unreliable) {
-				if (reliability->maxPacketLifeTime > 0) {
-					dci.reliability.type = Reliability::Type::Timed;
-					dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
-				} else {
-					dci.reliability.type = Reliability::Type::Rexmit;
-					dci.reliability.rexmit = reliability->maxRetransmits;
-				}
-			} else {
-				dci.reliability.type = Reliability::Type::Reliable;
+				if (reliability->maxPacketLifeTime > 0)
+					dci.reliability.maxPacketLifeTime.emplace(milliseconds(reliability->maxPacketLifeTime));
+				else
+					dci.reliability.maxRetransmits.emplace(reliability->maxRetransmits);
 			}
 			}
 
 
 			dci.negotiated = init->negotiated;
 			dci.negotiated = init->negotiated;
@@ -962,12 +966,12 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 		Reliability dcr = dataChannel->reliability();
 		Reliability dcr = dataChannel->reliability();
 		std::memset(reliability, 0, sizeof(*reliability));
 		std::memset(reliability, 0, sizeof(*reliability));
 		reliability->unordered = dcr.unordered;
 		reliability->unordered = dcr.unordered;
-		if (dcr.type == Reliability::Type::Timed) {
+		if(dcr.maxPacketLifeTime) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxPacketLifeTime = int(std::get<milliseconds>(dcr.rexmit).count());
-		} else if (dcr.type == Reliability::Type::Rexmit) {
+			reliability->maxPacketLifeTime = static_cast<unsigned int>(dcr.maxPacketLifeTime->count());
+		} else if (dcr.maxRetransmits) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxRetransmits = std::get<int>(dcr.rexmit);
+			reliability->maxRetransmits = *dcr.maxRetransmits;
 		} else {
 		} else {
 			reliability->unreliable = false;
 			reliability->unreliable = false;
 		}
 		}
@@ -1004,7 +1008,9 @@ int rtcAddTrackEx(int pc, const rtcTrackInit *init) {
 			mid = string(init->mid);
 			mid = string(init->mid);
 		} else {
 		} else {
 			switch (init->codec) {
 			switch (init->codec) {
+			case RTC_CODEC_AV1:
 			case RTC_CODEC_H264:
 			case RTC_CODEC_H264:
+			case RTC_CODEC_H265:
 			case RTC_CODEC_VP8:
 			case RTC_CODEC_VP8:
 			case RTC_CODEC_VP9:
 			case RTC_CODEC_VP9:
 				mid = "video";
 				mid = "video";
@@ -1012,6 +1018,7 @@ int rtcAddTrackEx(int pc, const rtcTrackInit *init) {
 			case RTC_CODEC_OPUS:
 			case RTC_CODEC_OPUS:
 			case RTC_CODEC_PCMU:
 			case RTC_CODEC_PCMU:
 			case RTC_CODEC_PCMA:
 			case RTC_CODEC_PCMA:
+			case RTC_CODEC_AAC:
 				mid = "audio";
 				mid = "audio";
 				break;
 				break;
 			default:
 			default:
@@ -1020,62 +1027,76 @@ int rtcAddTrackEx(int pc, const rtcTrackInit *init) {
 			}
 			}
 		}
 		}
 
 
-		optional<Description::Media> optDescription = nullopt;
+		int pt = init->payloadType;
+		auto profile = init->profile ? std::make_optional(string(init->profile)) : nullopt;
 
 
+		unique_ptr<Description::Media> description;
 		switch (init->codec) {
 		switch (init->codec) {
+		case RTC_CODEC_AV1:
 		case RTC_CODEC_H264:
 		case RTC_CODEC_H264:
+		case RTC_CODEC_H265:
 		case RTC_CODEC_VP8:
 		case RTC_CODEC_VP8:
 		case RTC_CODEC_VP9: {
 		case RTC_CODEC_VP9: {
-			auto desc = Description::Video(mid, direction);
+			auto video = std::make_unique<Description::Video>(mid, direction);
 			switch (init->codec) {
 			switch (init->codec) {
+			case RTC_CODEC_AV1:
+				video->addAV1Codec(pt, profile);
+				break;
 			case RTC_CODEC_H264:
 			case RTC_CODEC_H264:
-				desc.addH264Codec(init->payloadType);
+				video->addH264Codec(pt, profile);
+				break;
+			case RTC_CODEC_H265:
+				video->addH265Codec(pt, profile);
 				break;
 				break;
 			case RTC_CODEC_VP8:
 			case RTC_CODEC_VP8:
-				desc.addVP8Codec(init->payloadType);
+				video->addVP8Codec(pt, profile);
 				break;
 				break;
 			case RTC_CODEC_VP9:
 			case RTC_CODEC_VP9:
-				desc.addVP9Codec(init->payloadType);
+				video->addVP9Codec(pt, profile);
 				break;
 				break;
 			default:
 			default:
 				break;
 				break;
 			}
 			}
-			optDescription = desc;
+			description = std::move(video);
 			break;
 			break;
 		}
 		}
 		case RTC_CODEC_OPUS:
 		case RTC_CODEC_OPUS:
 		case RTC_CODEC_PCMU:
 		case RTC_CODEC_PCMU:
-		case RTC_CODEC_PCMA: {
-			auto desc = Description::Audio(mid, direction);
+		case RTC_CODEC_PCMA:
+		case RTC_CODEC_AAC: {
+			auto audio = std::make_unique<Description::Audio>(mid, direction);
 			switch (init->codec) {
 			switch (init->codec) {
 			case RTC_CODEC_OPUS:
 			case RTC_CODEC_OPUS:
-				desc.addOpusCodec(init->payloadType);
+				audio->addOpusCodec(pt, profile);
 				break;
 				break;
 			case RTC_CODEC_PCMU:
 			case RTC_CODEC_PCMU:
-				desc.addPCMUCodec(init->payloadType);
+				audio->addPCMUCodec(pt, profile);
 				break;
 				break;
 			case RTC_CODEC_PCMA:
 			case RTC_CODEC_PCMA:
-				desc.addPCMACodec(init->payloadType);
+				audio->addPCMACodec(pt, profile);
+				break;
+			case RTC_CODEC_AAC:
+				audio->addAACCodec(pt, profile);
 				break;
 				break;
 			default:
 			default:
 				break;
 				break;
 			}
 			}
-			optDescription = desc;
+			description = std::move(audio);
 			break;
 			break;
 		}
 		}
 		default:
 		default:
 			break;
 			break;
 		}
 		}
 
 
-		if (!optDescription)
+		if (!description)
 			throw std::invalid_argument("Unexpected codec");
 			throw std::invalid_argument("Unexpected codec");
 
 
-		auto desc = std::move(*optDescription);
-		desc.addSSRC(init->ssrc, init->name ? std::make_optional(string(init->name)) : nullopt,
-		             init->msid ? std::make_optional(string(init->msid)) : nullopt,
-		             init->trackId ? std::make_optional(string(init->trackId)) : nullopt);
+		description->addSSRC(init->ssrc,
+		                     init->name ? std::make_optional(string(init->name)) : nullopt,
+		                     init->msid ? std::make_optional(string(init->msid)) : nullopt,
+		                     init->trackId ? std::make_optional(string(init->trackId)) : nullopt);
 
 
-		int tr = emplaceTrack(peerConnection->addTrack(std::move(desc)));
+		int tr = emplaceTrack(peerConnection->addTrack(std::move(*description)));
 
 
 		if (auto ptr = getUserPointer(pc))
 		if (auto ptr = getUserPointer(pc))
 			rtcSetUserPointer(tr, *ptr);
 			rtcSetUserPointer(tr, *ptr);
@@ -1118,6 +1139,22 @@ int rtcGetTrackDirection(int tr, rtcDirection *direction) {
 	});
 	});
 }
 }
 
 
+int rtcRequestKeyframe(int tr) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		track->requestKeyframe();
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcRequestBitrate(int tr, unsigned int bitrate) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		track->requestBitrate(bitrate);
+		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,
@@ -1174,61 +1211,119 @@ int rtcSetMediaInterceptorCallback(int pc, rtcInterceptorCallbackFunc cb) {
 	});
 	});
 }
 }
 
 
-int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+int rtcSetH264Packetizer(int tr, const rtcPacketizerInit *init) {
 	return wrap([&] {
 	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		// create RTP configuration
 		// create RTP configuration
 		auto rtpConfig = createRtpPacketizationConfig(init);
 		auto rtpConfig = createRtpPacketizationConfig(init);
+		emplaceRtpConfig(rtpConfig, tr);
 		// create packetizer
 		// create packetizer
 		auto nalSeparator = init ? init->nalSeparator : RTC_NAL_SEPARATOR_LENGTH;
 		auto nalSeparator = init ? init->nalSeparator : RTC_NAL_SEPARATOR_LENGTH;
 		auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
 		auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
-		                                                     : RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE;
+		                                                     : RTC_DEFAULT_MAX_FRAGMENT_SIZE;
 		auto packetizer = std::make_shared<H264RtpPacketizer>(
 		auto packetizer = std::make_shared<H264RtpPacketizer>(
-		    static_cast<rtc::H264RtpPacketizer::Separator>(nalSeparator), rtpConfig,
-		    maxFragmentSize);
-		// create H264 handler
-		auto h264Handler = std::make_shared<H264PacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(h264Handler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(h264Handler);
+		    static_cast<rtc::NalUnit::Separator>(nalSeparator), rtpConfig, maxFragmentSize);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
-int rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) {
+int rtcSetH265Packetizer(int tr, const rtcPacketizerInit *init) {
 	return wrap([&] {
 	return wrap([&] {
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		// create RTP configuration
 		// create RTP configuration
 		auto rtpConfig = createRtpPacketizationConfig(init);
 		auto rtpConfig = createRtpPacketizationConfig(init);
 		// create packetizer
 		// create packetizer
-		auto packetizer = std::make_shared<OpusRtpPacketizer>(rtpConfig);
-		// create Opus handler
-		auto opusHandler = std::make_shared<OpusPacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(opusHandler, tr);
+		auto nalSeparator = init ? init->nalSeparator : RTC_NAL_SEPARATOR_LENGTH;
+		auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
+		                                                     : RTC_DEFAULT_MAX_FRAGMENT_SIZE;
+		auto packetizer = std::make_shared<H265RtpPacketizer>(
+		    static_cast<rtc::NalUnit::Separator>(nalSeparator), rtpConfig, maxFragmentSize);
+		track->setMediaHandler(packetizer);
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcSetAV1Packetizer(int tr, const rtcPacketizerInit *init) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		// create RTP configuration
+		auto rtpConfig = createRtpPacketizationConfig(init);
+		// create packetizer
+		auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
+		                                                     : RTC_DEFAULT_MAX_FRAGMENT_SIZE;
+		auto packetization = init->obuPacketization == RTC_OBU_PACKETIZED_TEMPORAL_UNIT
+		                         ? AV1RtpPacketizer::Packetization::TemporalUnit
+		                         : AV1RtpPacketizer::Packetization::Obu;
+		auto packetizer =
+		    std::make_shared<AV1RtpPacketizer>(packetization, rtpConfig, maxFragmentSize);
+		track->setMediaHandler(packetizer);
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcSetOpusPacketizer(int tr, const rtcPacketizerInit *init) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		// create RTP configuration
+		auto rtpConfig = createRtpPacketizationConfig(init);
 		emplaceRtpConfig(rtpConfig, tr);
 		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(opusHandler);
+		// create packetizer
+		auto packetizer = std::make_shared<OpusRtpPacketizer>(rtpConfig);
+		track->setMediaHandler(packetizer);
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcSetAACPacketizer(int tr, const rtcPacketizerInit *init) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		// create RTP configuration
+		auto rtpConfig = createRtpPacketizationConfig(init);
+		// create packetizer
+		auto packetizer = std::make_shared<AACRtpPacketizer>(rtpConfig);
+		track->setMediaHandler(packetizer);
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcChainRtcpReceivingSession(int tr) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		auto session = std::make_shared<rtc::RtcpReceivingSession>();
+		track->chainMediaHandler(session);
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcChainRtcpSrReporter(int tr) {
 int rtcChainRtcpSrReporter(int tr) {
-	return wrap([tr] {
+	return wrap([&] {
+		auto track = getTrack(tr);
 		auto config = getRtpConfig(tr);
 		auto config = getRtpConfig(tr);
 		auto reporter = std::make_shared<RtcpSrReporter>(config);
 		auto reporter = std::make_shared<RtcpSrReporter>(config);
+		track->chainMediaHandler(reporter);
 		emplaceRtcpSrReporter(reporter, tr);
 		emplaceRtcpSrReporter(reporter, tr);
-		auto chainableHandler = getMediaChainableHandler(tr);
-		chainableHandler->addToChain(reporter);
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
 int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
 int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
-	return wrap([tr, maxStoredPacketsCount] {
+	return wrap([&] {
+		auto track = getTrack(tr);
 		auto responder = std::make_shared<RtcpNackResponder>(maxStoredPacketsCount);
 		auto responder = std::make_shared<RtcpNackResponder>(maxStoredPacketsCount);
-		auto chainableHandler = getMediaChainableHandler(tr);
-		chainableHandler->addToChain(responder);
+		track->chainMediaHandler(responder);
+		return RTC_ERR_SUCCESS;
+	});
+}
+
+int rtcChainPliHandler(int tr, rtcPliHandlerCallbackFunc cb) {
+	return wrap([&] {
+		auto track = getTrack(tr);
+		auto handler = std::make_shared<PliHandler>([tr, cb] {
+			if (auto ptr = getUserPointer(tr))
+				cb(tr, *ptr);
+		});
+		track->chainMediaHandler(handler);
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
@@ -1236,7 +1331,9 @@ int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
 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);
+		if (timestamp)
+			*timestamp = config->secondsToTimestamp(seconds);
+
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
@@ -1244,7 +1341,9 @@ int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp)
 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);
+		if (seconds)
+			*seconds = config->timestampToSeconds(timestamp);
+
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
@@ -1252,7 +1351,9 @@ int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds)
 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;
+		if (timestamp)
+			*timestamp = config->timestamp;
+
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
@@ -1268,7 +1369,9 @@ int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp) {
 int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
 int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
 	return wrap([&] {
 	return wrap([&] {
 		auto sender = getRtcpSrReporter(id);
 		auto sender = getRtcpSrReporter(id);
-		*timestamp = sender->lastReportedTimestamp();
+		if (timestamp)
+			*timestamp = sender->lastReportedTimestamp();
+
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
@@ -1401,6 +1504,9 @@ int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *config) {
 		else if (config->maxOutstandingPings < 0)
 		else if (config->maxOutstandingPings < 0)
 			c.maxOutstandingPings = 0; // setting to 0 disables, not setting keeps default
 			c.maxOutstandingPings = 0; // setting to 0 disables, not setting keeps default
 
 
+		if(config->maxMessageSize > 0)
+			c.maxMessageSize = size_t(config->maxMessageSize);
+
 		auto webSocket = std::make_shared<WebSocket>(std::move(c));
 		auto webSocket = std::make_shared<WebSocket>(std::move(c));
 		webSocket->open(url);
 		webSocket->open(url);
 		return emplaceWebSocket(webSocket);
 		return emplaceWebSocket(webSocket);
@@ -1455,6 +1561,10 @@ RTC_C_EXPORT int rtcCreateWebSocketServer(const rtcWsServerConfiguration *config
 		c.keyPemFile = config->keyPemFile ? make_optional(string(config->keyPemFile)) : nullopt;
 		c.keyPemFile = config->keyPemFile ? make_optional(string(config->keyPemFile)) : nullopt;
 		c.keyPemPass = config->keyPemPass ? make_optional(string(config->keyPemPass)) : nullopt;
 		c.keyPemPass = config->keyPemPass ? make_optional(string(config->keyPemPass)) : nullopt;
 		c.bindAddress = config->bindAddress ? make_optional(string(config->bindAddress)) : nullopt;
 		c.bindAddress = config->bindAddress ? make_optional(string(config->bindAddress)) : nullopt;
+
+		if(config->maxMessageSize > 0)
+			c.maxMessageSize = size_t(config->maxMessageSize);
+
 		auto webSocketServer = std::make_shared<WebSocketServer>(std::move(c));
 		auto webSocketServer = std::make_shared<WebSocketServer>(std::move(c));
 		int wsserver = emplaceWebSocketServer(webSocketServer);
 		int wsserver = emplaceWebSocketServer(webSocketServer);
 
 

+ 2 - 4
src/channel.cpp

@@ -17,7 +17,7 @@ Channel::~Channel() { impl()->resetCallbacks(); }
 
 
 Channel::Channel(impl_ptr<impl::Channel> impl) : CheshireCat<impl::Channel>(std::move(impl)) {}
 Channel::Channel(impl_ptr<impl::Channel> impl) : CheshireCat<impl::Channel>(std::move(impl)) {}
 
 
-size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
+size_t Channel::maxMessageSize() const { return 0; }
 
 
 size_t Channel::bufferedAmount() const { return impl()->bufferedAmount; }
 size_t Channel::bufferedAmount() const { return impl()->bufferedAmount; }
 
 
@@ -49,9 +49,7 @@ void Channel::setBufferedAmountLowThreshold(size_t amount) {
 	impl()->bufferedAmountLowThreshold = amount;
 	impl()->bufferedAmountLowThreshold = amount;
 }
 }
 
 
-void Channel::resetCallbacks() {
-	impl()->resetCallbacks();
-}
+void Channel::resetCallbacks() { impl()->resetCallbacks(); }
 
 
 optional<message_variant> Channel::receive() { return impl()->receive(); }
 optional<message_variant> Channel::receive() { return impl()->receive(); }
 
 

+ 1 - 1
src/configuration.cpp

@@ -70,7 +70,7 @@ IceServer::IceServer(const string &url) {
 	password = utils::url_decode(opt[8].value_or(""));
 	password = utils::url_decode(opt[8].value_or(""));
 
 
 	hostname = opt[10].value();
 	hostname = opt[10].value();
-	if(hostname.front() == '[' && hostname.back() == ']') {
+	if (hostname.front() == '[' && hostname.back() == ']') {
 		// IPv6 literal
 		// IPv6 literal
 		hostname.erase(hostname.begin());
 		hostname.erase(hostname.begin());
 		hostname.pop_back();
 		hostname.pop_back();

+ 1 - 7
src/datachannel.cpp

@@ -26,13 +26,7 @@ DataChannel::DataChannel(impl_ptr<impl::DataChannel> impl)
     : CheshireCat<impl::DataChannel>(impl),
     : CheshireCat<impl::DataChannel>(impl),
       Channel(std::dynamic_pointer_cast<impl::Channel>(impl)) {}
       Channel(std::dynamic_pointer_cast<impl::Channel>(impl)) {}
 
 
-DataChannel::~DataChannel() {
-	try {
-		close();
-	} catch (const std::exception &e) {
-		PLOG_ERROR << e.what();
-	}
-}
+DataChannel::~DataChannel() {}
 
 
 void DataChannel::close() { return impl()->close(); }
 void DataChannel::close() { return impl()->close(); }
 
 

+ 238 - 69
src/description.cpp

@@ -33,17 +33,19 @@ inline bool match_prefix(string_view str, string_view prefix) {
 	       std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
 	       std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
 }
 }
 
 
-inline void trim_begin(string &str) {
-	str.erase(str.begin(),
-	          std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); }));
-}
-
 inline void trim_end(string &str) {
 inline void trim_end(string &str) {
 	str.erase(
 	str.erase(
 	    std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
 	    std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
 	    str.end());
 	    str.end());
 }
 }
 
 
+inline string get_first_line(const string &str) {
+	string line;
+	std::istringstream ss(str);
+	std::getline(ss, line);
+	return line;
+}
+
 inline std::pair<string_view, string_view> parse_pair(string_view attr) {
 inline std::pair<string_view, string_view> parse_pair(string_view attr) {
 	string_view key, value;
 	string_view key, value;
 	if (size_t separator = attr.find(':'); separator != string::npos) {
 	if (size_t separator = attr.find(':'); separator != string::npos) {
@@ -64,22 +66,6 @@ template <typename T> T to_integer(string_view s) {
 	}
 	}
 }
 }
 
 
-inline bool is_sha256_fingerprint(string_view f) {
-	if (f.size() != 32 * 3 - 1)
-		return false;
-
-	for (size_t i = 0; i < f.size(); ++i) {
-		if (i % 3 == 2) {
-			if (f[i] != ':')
-				return false;
-		} else {
-			if (!std::isxdigit(f[i]))
-				return false;
-		}
-	}
-	return true;
-}
-
 } // namespace
 } // namespace
 
 
 namespace rtc {
 namespace rtc {
@@ -120,19 +106,58 @@ Description::Description(const string &sdp, Type type, Role role)
 					mRole = Role::ActPass;
 					mRole = Role::ActPass;
 
 
 			} else if (key == "fingerprint") {
 			} else if (key == "fingerprint") {
-				if (match_prefix(value, "sha-256 ")) {
-					string fingerprint{value.substr(8)};
-					trim_begin(fingerprint);
-					setFingerprint(std::move(fingerprint));
-				} else {
-					PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
+				// RFC 8122: The fingerprint attribute may be either a session-level or a
+				// media-level SDP attribute. If it is a session-level attribute, it applies to all
+				// TLS sessions for which no media-level fingerprint attribute is defined.
+				if (!mFingerprint || index == 0) { // first media overrides session-level
+					auto fingerprintExploded = utils::explode(string(value), ' ');
+					if (fingerprintExploded.size() != 2) {
+						PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
+						continue;
+					}
+
+					auto first = fingerprintExploded.at(0);
+					std::transform(first.begin(), first.end(), first.begin(),
+					               [](char c) { return char(std::tolower(c)); });
+
+					std::optional<CertificateFingerprint::Algorithm> fingerprintAlgorithm;
+
+					for (auto a : std::array<CertificateFingerprint::Algorithm, 5>{
+					         CertificateFingerprint::Algorithm::Sha1,
+					         CertificateFingerprint::Algorithm::Sha224,
+					         CertificateFingerprint::Algorithm::Sha256,
+					         CertificateFingerprint::Algorithm::Sha384,
+					         CertificateFingerprint::Algorithm::Sha512}) {
+						if (first == CertificateFingerprint::AlgorithmIdentifier(a)) {
+							fingerprintAlgorithm = a;
+							break;
+						}
+					}
+
+					if (fingerprintAlgorithm.has_value()) {
+						setFingerprint(CertificateFingerprint{
+						    fingerprintAlgorithm.value(), std::move(fingerprintExploded.at(1))});
+					} else {
+						PLOG_WARNING << "Unknown certificate fingerprint algorithm: " << first;
+					}
 				}
 				}
 			} else if (key == "ice-ufrag") {
 			} else if (key == "ice-ufrag") {
-				mIceUfrag = value;
+				// RFC 8839: The "ice-pwd" and "ice-ufrag" attributes can appear at either the
+				// session-level or media-level. When present in both, the value in the media-level
+				// takes precedence.
+				if (!mIceUfrag || index == 0) // media-level for first media overrides session-level
+					mIceUfrag = value;
 			} else if (key == "ice-pwd") {
 			} else if (key == "ice-pwd") {
-				mIcePwd = value;
+				// RFC 8839: The "ice-pwd" and "ice-ufrag" attributes can appear at either the
+				// session-level or media-level. When present in both, the value in the media-level
+				// takes precedence.
+				if (!mIcePwd || index == 0) // media-level for first media overrides session-level
+					mIcePwd = value;
 			} else if (key == "ice-options") {
 			} else if (key == "ice-options") {
-				mIceOptions = utils::explode(string(value), ',');
+				// RFC 8839: The "ice-options" attribute is a session-level and media-level
+				// attribute.
+				if (mIceOptions.empty())
+					mIceOptions = utils::explode(string(value), ',');
 			} else if (key == "candidate") {
 			} else if (key == "candidate") {
 				addCandidate(Candidate(attr, bundleMid()));
 				addCandidate(Candidate(attr, bundleMid()));
 			} else if (key == "end-of-candidates") {
 			} else if (key == "end-of-candidates") {
@@ -182,7 +207,7 @@ std::vector<string> Description::iceOptions() const { return mIceOptions; }
 
 
 optional<string> Description::icePwd() const { return mIcePwd; }
 optional<string> Description::icePwd() const { return mIcePwd; }
 
 
-optional<string> Description::fingerprint() const { return mFingerprint; }
+optional<CertificateFingerprint> Description::fingerprint() const { return mFingerprint; }
 
 
 bool Description::ended() const { return mEnded; }
 bool Description::ended() const { return mEnded; }
 
 
@@ -191,13 +216,13 @@ void Description::hintType(Type type) {
 		mType = type;
 		mType = type;
 }
 }
 
 
-void Description::setFingerprint(string fingerprint) {
-	if (!is_sha256_fingerprint(fingerprint))
-		throw std::invalid_argument("Invalid SHA256 fingerprint \"" + fingerprint + "\"");
+void Description::setFingerprint(CertificateFingerprint f) {
+	if (!f.isValid())
+		throw std::invalid_argument("Invalid " + CertificateFingerprint::AlgorithmIdentifier(f.algorithm) + " fingerprint \"" + f.value + "\"");
 
 
-	std::transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
+	std::transform(f.value.begin(), f.value.end(), f.value.begin(),
 	               [](char c) { return char(std::toupper(c)); });
 	               [](char c) { return char(std::toupper(c)); });
-	mFingerprint.emplace(std::move(fingerprint));
+	mFingerprint = std::move(f);
 }
 }
 
 
 void Description::addIceOption(string option) {
 void Description::addIceOption(string option) {
@@ -292,7 +317,9 @@ string Description::generateSdp(string_view eol) const {
 	if (!mIceOptions.empty())
 	if (!mIceOptions.empty())
 		sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
 		sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
 	if (mFingerprint)
 	if (mFingerprint)
-		sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
+		sdp << "a=fingerprint:"
+		    << CertificateFingerprint::AlgorithmIdentifier(mFingerprint->algorithm) << " "
+		    << mFingerprint->value << eol;
 
 
 	for (const auto &attr : mAttributes)
 	for (const auto &attr : mAttributes)
 		sdp << "a=" << attr << eol;
 		sdp << "a=" << attr << eol;
@@ -355,7 +382,9 @@ string Description::generateApplicationSdp(string_view eol) const {
 	if (!mIceOptions.empty())
 	if (!mIceOptions.empty())
 		sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
 		sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
 	if (mFingerprint)
 	if (mFingerprint)
-		sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
+		sdp << "a=fingerprint:"
+		    << CertificateFingerprint::AlgorithmIdentifier(mFingerprint->algorithm) << " "
+		    << mFingerprint->value << eol;
 
 
 	for (const auto &attr : mAttributes)
 	for (const auto &attr : mAttributes)
 		sdp << "a=" << attr << eol;
 		sdp << "a=" << attr << eol;
@@ -507,12 +536,15 @@ unsigned int Description::mediaCount() const { return unsigned(mEntries.size());
 Description::Entry::Entry(const string &mline, string mid, Direction dir)
 Description::Entry::Entry(const string &mline, string mid, Direction dir)
     : mMid(std::move(mid)), mDirection(dir) {
     : mMid(std::move(mid)), mDirection(dir) {
 
 
-	uint16_t port;
-	std::istringstream ss(mline);
+	uint16_t port = 0;
+	std::istringstream ss(match_prefix(mline, "m=") ? mline.substr(2) : mline);
 	ss >> mType;
 	ss >> mType;
 	ss >> port;
 	ss >> port;
 	ss >> mDescription;
 	ss >> mDescription;
 
 
+	if (mType.empty() || mDescription.empty())
+		throw std::invalid_argument("Invalid media description line");
+
 	// RFC 3264: Existing media streams are removed by creating a new SDP with the port number for
 	// RFC 3264: Existing media streams are removed by creating a new SDP with the port number for
 	// that stream set to zero.
 	// that stream set to zero.
 	// RFC 8843: If the offerer assigns a zero port value to a bundled "m=" section, but does not
 	// RFC 8843: If the offerer assigns a zero port value to a bundled "m=" section, but does not
@@ -521,8 +553,18 @@ Description::Entry::Entry(const string &mline, string mid, Direction dir)
 	mIsRemoved = (port == 0);
 	mIsRemoved = (port == 0);
 }
 }
 
 
+string Description::Entry::type() const { return mType; }
+
+string Description::Entry::description() const { return mDescription; }
+
+string Description::Entry::mid() const { return mMid; }
+
+Description::Direction Description::Entry::direction() const { return mDirection; }
+
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 
 
+bool Description::Entry::isRemoved() const { return mIsRemoved; }
+
 void Description::Entry::markRemoved() { mIsRemoved = true; }
 void Description::Entry::markRemoved() { mIsRemoved = true; }
 
 
 std::vector<string> Description::attributes() const { return mAttributes; }
 std::vector<string> Description::attributes() const { return mAttributes; }
@@ -532,6 +574,8 @@ void Description::addAttribute(string attr) {
 		mAttributes.emplace_back(std::move(attr));
 		mAttributes.emplace_back(std::move(attr));
 }
 }
 
 
+void Description::Entry::addRid(string rid) { mRids.emplace_back(rid); }
+
 void Description::removeAttribute(const string &attr) {
 void Description::removeAttribute(const string &attr) {
 	mAttributes.erase(
 	mAttributes.erase(
 	    std::remove_if(mAttributes.begin(), mAttributes.end(),
 	    std::remove_if(mAttributes.begin(), mAttributes.end(),
@@ -555,6 +599,14 @@ Description::Entry::ExtMap *Description::Entry::extMap(int id) {
 	return &it->second;
 	return &it->second;
 }
 }
 
 
+const Description::Entry::ExtMap *Description::Entry::extMap(int id) const {
+	auto it = mExtMaps.find(id);
+	if (it == mExtMaps.end())
+		throw std::invalid_argument("extmap not found");
+
+	return &it->second;
+}
+
 void Description::Entry::addExtMap(ExtMap map) {
 void Description::Entry::addExtMap(ExtMap map) {
 	auto id = map.id;
 	auto id = map.id;
 	mExtMaps.emplace(id, std::move(map));
 	mExtMaps.emplace(id, std::move(map));
@@ -597,8 +649,34 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 	if (mDirection != Direction::Unknown)
 	if (mDirection != Direction::Unknown)
 		sdp << "a=" << mDirection << eol;
 		sdp << "a=" << mDirection << eol;
 
 
-	for (const auto &attr : mAttributes)
+	for (const auto &attr : mAttributes) {
+		if (mRids.size() != 0 && match_prefix(attr, "ssrc:")) {
+			continue;
+		}
+
 		sdp << "a=" << attr << eol;
 		sdp << "a=" << attr << eol;
+	}
+
+	for (const auto &rid : mRids) {
+		sdp << "a=rid:" << rid << " send" << eol;
+	}
+
+	if (mRids.size() != 0) {
+		sdp << "a=simulcast:send ";
+
+		bool first = true;
+		for (const auto &rid : mRids) {
+			if (first) {
+				first = false;
+			} else {
+				sdp << ";";
+			}
+
+			sdp << rid;
+		}
+
+		sdp << eol;
+	}
 
 
 	return sdp.str();
 	return sdp.str();
 }
 }
@@ -653,7 +731,7 @@ Description::Entry::ExtMap::ExtMap(string_view description) { setDescription(des
 void Description::Entry::ExtMap::setDescription(string_view description) {
 void Description::Entry::ExtMap::setDescription(string_view description) {
 	const size_t uriStart = description.find(' ');
 	const size_t uriStart = description.find(' ');
 	if (uriStart == string::npos)
 	if (uriStart == string::npos)
-		throw std::invalid_argument("Invalid description");
+		throw std::invalid_argument("Invalid description for extmap");
 
 
 	const string_view idAndDirection = description.substr(0, uriStart);
 	const string_view idAndDirection = description.substr(0, uriStart);
 	const size_t idSplit = idAndDirection.find('/');
 	const size_t idSplit = idAndDirection.find('/');
@@ -672,7 +750,7 @@ void Description::Entry::ExtMap::setDescription(string_view description) {
 		else if (directionStr == "inactive")
 		else if (directionStr == "inactive")
 			this->direction = Direction::Inactive;
 			this->direction = Direction::Inactive;
 		else
 		else
-			throw std::invalid_argument("Invalid direction");
+			throw std::invalid_argument("Invalid direction for extmap");
 	}
 	}
 
 
 	const string_view uriAndAttributes = description.substr(uriStart + 1);
 	const string_view uriAndAttributes = description.substr(uriStart + 1);
@@ -695,9 +773,11 @@ void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<
 		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 + " " +
 		mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " +
 		                         trackId.value_or(*msid));
 		                         trackId.value_or(*msid));
+		mAttributes.emplace_back("msid:" + *msid + " " + trackId.value_or(*msid));
+	}
 
 
 	mSsrcs.emplace_back(ssrc);
 	mSsrcs.emplace_back(ssrc);
 }
 }
@@ -762,6 +842,16 @@ Description::Application Description::Application::reciprocate() const {
 	return reciprocated;
 	return reciprocated;
 }
 }
 
 
+void Description::Application::setSctpPort(uint16_t port) { mSctpPort = port; }
+
+void Description::Application::hintSctpPort(uint16_t port) { mSctpPort = mSctpPort.value_or(port); }
+
+void Description::Application::setMaxMessageSize(size_t size) { mMaxMessageSize = size; }
+
+optional<uint16_t> Description::Application::sctpPort() const { return mSctpPort; }
+
+optional<size_t> Description::Application::maxMessageSize() const { return mMaxMessageSize; }
+
 string Description::Application::generateSdpLines(string_view eol) const {
 string Description::Application::generateSdpLines(string_view eol) const {
 	std::ostringstream sdp;
 	std::ostringstream sdp;
 	sdp << Entry::generateSdpLines(eol);
 	sdp << Entry::generateSdpLines(eol);
@@ -792,10 +882,11 @@ void Description::Application::parseSdpLine(string_view line) {
 	}
 	}
 }
 }
 
 
-Description::Media::Media(const string &sdp) : Entry(sdp, "", Direction::Unknown) {
+Description::Media::Media(const string &sdp) : Entry(get_first_line(sdp), "", Direction::Unknown) {
+	string line;
 	std::istringstream ss(sdp);
 	std::istringstream ss(sdp);
+	std::getline(ss, line); // discard first line
 	while (ss) {
 	while (ss) {
-		string line;
 		std::getline(ss, line);
 		std::getline(ss, line);
 		trim_end(line);
 		trim_end(line);
 		if (line.empty())
 		if (line.empty())
@@ -805,7 +896,7 @@ Description::Media::Media(const string &sdp) : Entry(sdp, "", Direction::Unknown
 	}
 	}
 
 
 	if (mid().empty())
 	if (mid().empty())
-		throw std::invalid_argument("Missing mid in media SDP");
+		throw std::invalid_argument("Missing mid in media description");
 }
 }
 
 
 Description::Media::Media(const string &mline, string mid, Direction dir)
 Description::Media::Media(const string &mline, string mid, Direction dir)
@@ -887,6 +978,14 @@ Description::Media::RtpMap *Description::Media::rtpMap(int payloadType) {
 	return &it->second;
 	return &it->second;
 }
 }
 
 
+const Description::Media::RtpMap *Description::Media::rtpMap(int payloadType) const {
+	auto it = mRtpMaps.find(payloadType);
+	if (it == mRtpMaps.end())
+		throw std::invalid_argument("rtpmap not found");
+
+	return &it->second;
+}
+
 void Description::Media::addRtpMap(RtpMap map) {
 void Description::Media::addRtpMap(RtpMap map) {
 	auto payloadType = map.payloadType;
 	auto payloadType = map.payloadType;
 	mRtpMaps.emplace(payloadType, std::move(map));
 	mRtpMaps.emplace(payloadType, std::move(map));
@@ -1024,14 +1123,14 @@ Description::Media::RtpMap::RtpMap(string_view description) { setDescription(des
 void Description::Media::RtpMap::setDescription(string_view description) {
 void Description::Media::RtpMap::setDescription(string_view description) {
 	size_t p = description.find(' ');
 	size_t p = description.find(' ');
 	if (p == string::npos)
 	if (p == string::npos)
-		throw std::invalid_argument("Invalid format description");
+		throw std::invalid_argument("Invalid format description for rtpmap");
 
 
 	this->payloadType = to_integer<int>(description.substr(0, p));
 	this->payloadType = to_integer<int>(description.substr(0, p));
 
 
 	string_view line = description.substr(p + 1);
 	string_view line = description.substr(p + 1);
 	size_t spl = line.find('/');
 	size_t spl = line.find('/');
 	if (spl == string::npos)
 	if (spl == string::npos)
-		throw std::invalid_argument("Invalid format description");
+		throw std::invalid_argument("Invalid format description for rtpmap");
 
 
 	this->format = line.substr(0, spl);
 	this->format = line.substr(0, spl);
 
 
@@ -1094,7 +1193,7 @@ void Description::Audio::addAudioCodec(int payloadType, string codec, optional<s
 }
 }
 
 
 void Description::Audio::addOpusCodec(int payloadType, optional<string> profile) {
 void Description::Audio::addOpusCodec(int payloadType, optional<string> profile) {
-	addAudioCodec(payloadType, "OPUS", profile);
+	addAudioCodec(payloadType, "opus", profile);
 }
 }
 
 
 void Description::Audio::addPCMACodec(int payloadType, optional<string> profile) {
 void Description::Audio::addPCMACodec(int payloadType, optional<string> profile) {
@@ -1105,6 +1204,14 @@ void Description::Audio::addPCMUCodec(int payloadType, optional<string> profile)
 	addAudioCodec(payloadType, "PCMU", profile);
 	addAudioCodec(payloadType, "PCMU", profile);
 }
 }
 
 
+void Description::Audio::addAACCodec(int payloadType, optional<string> profile) {
+	if (profile) {
+		addAudioCodec(payloadType, "MP4A-LATM", profile);
+	} else {
+		addAudioCodec(payloadType, "MP4A-LATM", "cpresent=1");
+	}
+}
+
 Description::Video::Video(string mid, Direction dir)
 Description::Video::Video(string mid, Direction dir)
     : Media("video 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
     : Media("video 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
 
@@ -1138,16 +1245,24 @@ void Description::Video::addVideoCodec(int payloadType, string codec, optional<s
 	// ";rtx-time=3000"); addFormat(rtx);
 	// ";rtx-time=3000"); addFormat(rtx);
 }
 }
 
 
-void Description::Video::addH264Codec(int pt, optional<string> profile) {
-	addVideoCodec(pt, "H264", profile);
+void Description::Video::addH264Codec(int payloadType, optional<string> profile) {
+	addVideoCodec(payloadType, "H264", profile);
+}
+
+void Description::Video::addH265Codec(int payloadType, optional<string> profile) {
+	addVideoCodec(payloadType, "H265", profile);
+}
+
+void Description::Video::addVP8Codec(int payloadType, optional<string> profile) {
+	addVideoCodec(payloadType, "VP8", profile);
 }
 }
 
 
-void Description::Video::addVP8Codec(int payloadType) {
-	addVideoCodec(payloadType, "VP8", nullopt);
+void Description::Video::addVP9Codec(int payloadType, optional<string> profile) {
+	addVideoCodec(payloadType, "VP9", profile);
 }
 }
 
 
-void Description::Video::addVP9Codec(int payloadType) {
-	addVideoCodec(payloadType, "VP9", nullopt);
+void Description::Video::addAV1Codec(int payloadType, optional<string> profile) {
+	addVideoCodec(payloadType, "AV1", profile);
 }
 }
 
 
 Description::Type Description::stringToType(const string &typeString) {
 Description::Type Description::stringToType(const string &typeString) {
@@ -1178,18 +1293,70 @@ string Description::typeToString(Type type) {
 	}
 	}
 }
 }
 
 
-} // namespace rtc
+size_t
+CertificateFingerprint::AlgorithmSize(CertificateFingerprint::Algorithm fingerprintAlgorithm) {
+	switch (fingerprintAlgorithm) {
+	case CertificateFingerprint::Algorithm::Sha1:
+		return 20;
+	case CertificateFingerprint::Algorithm::Sha224:
+		return 28;
+	case CertificateFingerprint::Algorithm::Sha256:
+		return 32;
+	case CertificateFingerprint::Algorithm::Sha384:
+		return 48;
+	case CertificateFingerprint::Algorithm::Sha512:
+		return 64;
+    default:
+        return 0;
+	}
+}
+
+std::string CertificateFingerprint::AlgorithmIdentifier(
+    CertificateFingerprint::Algorithm fingerprintAlgorithm) {
+	switch (fingerprintAlgorithm) {
+	case CertificateFingerprint::Algorithm::Sha1:
+		return "sha-1";
+	case CertificateFingerprint::Algorithm::Sha224:
+		return "sha-224";
+	case CertificateFingerprint::Algorithm::Sha256:
+		return "sha-256";
+	case CertificateFingerprint::Algorithm::Sha384:
+		return "sha-256";
+	case CertificateFingerprint::Algorithm::Sha512:
+		return "sha-512";
+	default:
+	    return "unknown";
+	}
+}
+
+bool CertificateFingerprint::isValid() const {
+	size_t expectedSize = AlgorithmSize(this->algorithm);
+	if (expectedSize == 0 || this->value.size() != expectedSize * 3 - 1) {
+		return false;
+	}
 
 
-std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
-	return out << std::string(description);
+	for (size_t i = 0; i < this->value.size(); ++i) {
+		if (i % 3 == 2) {
+			if (this->value[i] != ':')
+				return false;
+		} else {
+			if (!std::isxdigit(this->value[i]))
+				return false;
+		}
+	}
+	return true;
+}
+
+std::ostream &operator<<(std::ostream &out, const Description &description) {
+	return out << string(description);
 }
 }
 
 
-std::ostream &operator<<(std::ostream &out, rtc::Description::Type type) {
-	return out << rtc::Description::typeToString(type);
+std::ostream &operator<<(std::ostream &out, Description::Type type) {
+	return out << Description::typeToString(type);
 }
 }
 
 
-std::ostream &operator<<(std::ostream &out, rtc::Description::Role role) {
-	using Role = rtc::Description::Role;
+std::ostream &operator<<(std::ostream &out, Description::Role role) {
+	using Role = Description::Role;
 	// Used for SDP generation, do not change
 	// Used for SDP generation, do not change
 	switch (role) {
 	switch (role) {
 	case Role::Active:
 	case Role::Active:
@@ -1205,25 +1372,27 @@ std::ostream &operator<<(std::ostream &out, rtc::Description::Role role) {
 	return out;
 	return out;
 }
 }
 
 
-std::ostream &operator<<(std::ostream &out, const rtc::Description::Direction &direction) {
+std::ostream &operator<<(std::ostream &out, const Description::Direction &direction) {
 	// Used for SDP generation, do not change
 	// Used for SDP generation, do not change
 	switch (direction) {
 	switch (direction) {
-	case rtc::Description::Direction::RecvOnly:
+	case Description::Direction::RecvOnly:
 		out << "recvonly";
 		out << "recvonly";
 		break;
 		break;
-	case rtc::Description::Direction::SendOnly:
+	case Description::Direction::SendOnly:
 		out << "sendonly";
 		out << "sendonly";
 		break;
 		break;
-	case rtc::Description::Direction::SendRecv:
+	case Description::Direction::SendRecv:
 		out << "sendrecv";
 		out << "sendrecv";
 		break;
 		break;
-	case rtc::Description::Direction::Inactive:
+	case Description::Direction::Inactive:
 		out << "inactive";
 		out << "inactive";
 		break;
 		break;
-	case rtc::Description::Direction::Unknown:
+	case Description::Direction::Unknown:
 	default:
 	default:
 		out << "unknown";
 		out << "unknown";
 		break;
 		break;
 	}
 	}
 	return out;
 	return out;
 }
 }
+
+} // namespace rtc

+ 14 - 23
src/global.cpp

@@ -7,6 +7,7 @@
  */
  */
 
 
 #include "plog/Appenders/ColorConsoleAppender.h"
 #include "plog/Appenders/ColorConsoleAppender.h"
+#include "plog/Converters/UTF8Converter.h"
 #include "plog/Formatters/FuncMessageFormatter.h"
 #include "plog/Formatters/FuncMessageFormatter.h"
 #include "plog/Formatters/TxtFormatter.h"
 #include "plog/Formatters/TxtFormatter.h"
 #include "plog/Init.h"
 #include "plog/Init.h"
@@ -19,11 +20,6 @@
 
 
 #include <mutex>
 #include <mutex>
 
 
-#ifdef _WIN32
-#include <codecvt>
-#include <locale>
-#endif
-
 namespace {
 namespace {
 
 
 void plogInit(plog::Severity severity, plog::IAppender *appender) {
 void plogInit(plog::Severity severity, plog::IAppender *appender) {
@@ -58,16 +54,11 @@ struct LogAppender : public plog::IAppender {
 		auto formatted = plog::FuncMessageFormatter::format(record);
 		auto formatted = plog::FuncMessageFormatter::format(record);
 		formatted.pop_back(); // remove newline
 		formatted.pop_back(); // remove newline
 
 
-#ifdef _WIN32
-		using convert_type = std::codecvt_utf8<wchar_t>;
-		std::wstring_convert<convert_type, wchar_t> converter;
-		std::string str = converter.to_bytes(formatted);
-#else
-		std::string str = formatted;
-#endif
+		const auto &converted =
+		    plog::UTF8Converter::convert(formatted); // does nothing on non-Windows systems
 
 
-		if (!callback(static_cast<LogLevel>(severity), str))
-			std::cout << plog::severityToString(severity) << " " << str << std::endl;
+		if (!callback(static_cast<LogLevel>(severity), converted))
+			std::cout << plog::severityToString(severity) << " " << converted << std::endl;
 	}
 	}
 };
 };
 
 
@@ -97,26 +88,24 @@ std::shared_future<void> Cleanup() { return impl::Init::Instance().cleanup(); }
 
 
 void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); }
 void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); }
 
 
-} // namespace rtc
-
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::LogLevel level) {
+RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level) {
 	switch (level) {
 	switch (level) {
-	case rtc::LogLevel::Fatal:
+	case LogLevel::Fatal:
 		out << "fatal";
 		out << "fatal";
 		break;
 		break;
-	case rtc::LogLevel::Error:
+	case LogLevel::Error:
 		out << "error";
 		out << "error";
 		break;
 		break;
-	case rtc::LogLevel::Warning:
+	case LogLevel::Warning:
 		out << "warning";
 		out << "warning";
 		break;
 		break;
-	case rtc::LogLevel::Info:
+	case LogLevel::Info:
 		out << "info";
 		out << "info";
 		break;
 		break;
-	case rtc::LogLevel::Debug:
+	case LogLevel::Debug:
 		out << "debug";
 		out << "debug";
 		break;
 		break;
-	case rtc::LogLevel::Verbose:
+	case LogLevel::Verbose:
 		out << "verbose";
 		out << "verbose";
 		break;
 		break;
 	default:
 	default:
@@ -125,3 +114,5 @@ RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::LogLevel level)
 	}
 	}
 	return out;
 	return out;
 }
 }
+
+} // namespace rtc

+ 0 - 20
src/h264packetizationhandler.cpp

@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "h264packetizationhandler.hpp"
-
-namespace rtc {
-
-H264PacketizationHandler::H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer)
-    : MediaChainableHandler(packetizer) {}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 144 - 0
src/h264rtpdepacketizer.cpp

@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2023 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#if RTC_ENABLE_MEDIA
+
+#include "h264rtpdepacketizer.hpp"
+#include "nalunit.hpp"
+#include "track.hpp"
+
+#include "impl/logcounter.hpp"
+
+#include <cmath>
+#include <utility>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace rtc {
+
+const unsigned long stapaHeaderSize = 1;
+const auto fuaHeaderSize = 2;
+
+const uint8_t naluTypeSTAPA = 24;
+const uint8_t naluTypeFUA = 28;
+
+message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
+                                                message_vector::iterator end, uint32_t timestamp) {
+	message_vector out = {};
+	auto fua_buffer = std::vector<std::byte>{};
+	auto frameInfo = std::make_shared<FrameInfo>(timestamp);
+
+	for (auto it = begin; it != end; it++) {
+		auto pkt = it->get();
+		auto pktParsed = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
+		auto headerSize =
+		    sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize();
+		auto nalUnitHeader = NalUnitHeader{std::to_integer<uint8_t>(pkt->at(headerSize))};
+
+		if (fua_buffer.size() != 0 || nalUnitHeader.unitType() == naluTypeFUA) {
+			if (fua_buffer.size() == 0) {
+				fua_buffer.push_back(std::byte(0));
+			}
+
+			auto nalUnitFragmentHeader =
+			    NalUnitFragmentHeader{std::to_integer<uint8_t>(pkt->at(headerSize + 1))};
+
+			std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(),
+			          std::back_inserter(fua_buffer));
+
+			if (nalUnitFragmentHeader.isEnd()) {
+				fua_buffer.at(0) =
+				    std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType());
+
+				out.push_back(
+				    make_message(std::move(fua_buffer), Message::Binary, 0, nullptr, frameInfo));
+				fua_buffer.clear();
+			}
+		} else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) {
+			out.push_back(make_message(pkt->begin() + headerSize, pkt->end(), Message::Binary, 0,
+			                           nullptr, frameInfo));
+		} else if (nalUnitHeader.unitType() == naluTypeSTAPA) {
+			auto currOffset = stapaHeaderSize + headerSize;
+
+			while (currOffset < pkt->size()) {
+				auto naluSize =
+				    uint16_t(pkt->at(currOffset)) << 8 | uint8_t(pkt->at(currOffset + 1));
+
+				currOffset += 2;
+
+				if (pkt->size() < currOffset + naluSize) {
+					throw std::runtime_error("STAP-A declared size is larger then buffer");
+				}
+
+				out.push_back(make_message(pkt->begin() + currOffset,
+				                           pkt->begin() + currOffset + naluSize, Message::Binary, 0,
+				                           nullptr, frameInfo));
+				currOffset += naluSize;
+			}
+		} else {
+			throw std::runtime_error("Unknown H264 RTP Packetization");
+		}
+	}
+
+	return out;
+}
+
+void H264RtpDepacketizer::incoming(message_vector &messages, const message_callback &) {
+	messages.erase(std::remove_if(messages.begin(), messages.end(),
+	                              [&](message_ptr message) {
+		                              if (message->type == Message::Control) {
+			                              return false;
+		                              }
+
+		                              if (message->size() < sizeof(RtpHeader)) {
+			                              PLOG_VERBOSE << "RTP packet is too small, size="
+			                                           << message->size();
+			                              return true;
+		                              }
+
+		                              mRtpBuffer.push_back(std::move(message));
+		                              return true;
+	                              }),
+	               messages.end());
+
+	while (mRtpBuffer.size() != 0) {
+		uint32_t current_timestamp = 0;
+		size_t packets_in_timestamp = 0;
+
+		for (const auto &pkt : mRtpBuffer) {
+			auto p = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
+
+			if (current_timestamp == 0) {
+				current_timestamp = p->timestamp();
+			} else if (current_timestamp != p->timestamp()) {
+				break;
+			}
+
+			packets_in_timestamp++;
+		}
+
+		if (packets_in_timestamp == mRtpBuffer.size()) {
+			break;
+		}
+
+		auto begin = mRtpBuffer.begin();
+		auto end = mRtpBuffer.begin() + (packets_in_timestamp - 1);
+
+		auto frames = buildFrames(begin, end + 1, current_timestamp);
+		messages.insert(messages.end(), frames.begin(), frames.end());
+		mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
+	}
+}
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA

+ 21 - 76
src/h264rtppacketizer.cpp

@@ -22,58 +22,6 @@
 
 
 namespace rtc {
 namespace rtc {
 
 
-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 == 0x00 && detectShort) {
-			return NUSM_secondZero;
-		} else if (byte == 0x01 && detectShort) {
-			return NUSM_shortMatch;
-		}
-		break;
-	case NUSM_thirdZero:
-		if (byte == 0x00 && detectLong) {
-			return NUSM_thirdZero;
-		} else 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) {
 shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 	auto nalus = std::make_shared<NalUnits>();
 	auto nalus = std::make_shared<NalUnits>();
 	if (separator == Separator::Length) {
 	if (separator == Separator::Length) {
@@ -103,7 +51,7 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 		NalUnitStartSequenceMatch match = NUSM_noMatch;
 		NalUnitStartSequenceMatch match = NUSM_noMatch;
 		size_t index = 0;
 		size_t index = 0;
 		while (index < message->size()) {
 		while (index < message->size()) {
-			match = StartSequenceMatchSucc(match, (*message)[index++], separator);
+			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index++], separator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				match = NUSM_noMatch;
 				match = NUSM_noMatch;
 				break;
 				break;
@@ -113,7 +61,7 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 		size_t naluStartIndex = index;
 		size_t naluStartIndex = index;
 
 
 		while (index < message->size()) {
 		while (index < message->size()) {
-			match = StartSequenceMatchSucc(match, (*message)[index], separator);
+			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index], separator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
 				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
 				size_t naluEndIndex = index - sequenceLength;
 				size_t naluEndIndex = index - sequenceLength;
@@ -133,33 +81,30 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 }
 }
 
 
 H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
 H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                     uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
+                                     uint16_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
       separator(Separator::Length) {}
       separator(Separator::Length) {}
 
 
-H264RtpPacketizer::H264RtpPacketizer(H264RtpPacketizer::Separator separator,
+H264RtpPacketizer::H264RtpPacketizer(Separator separator,
                                      shared_ptr<RtpPacketizationConfig> rtpConfig,
                                      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) {
+                                     uint16_t maxFragmentSize)
+    : RtpPacketizer(rtpConfig), maxFragmentSize(maxFragmentSize), separator(separator) {}
+
+void H264RtpPacketizer::outgoing(message_vector &messages, [[maybe_unused]] const message_callback &send) {
+	message_vector result;
+	for(const auto &message : messages) {
 		auto nalus = splitMessage(message);
 		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));
+		auto fragments = nalus->generateFragments(maxFragmentSize);
+		if (fragments.size() == 0)
+			continue;
+
+		for (size_t i = 0; i < fragments.size() - 1; i++)
+			result.push_back(packetize(fragments[i], false));
+
+		result.push_back(packetize(fragments[fragments.size() - 1], true));
 	}
 	}
-	return {packets, control};
+
+	messages.swap(result);
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc

+ 100 - 0
src/h265nalunit.cpp

@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2023 Zita Liao (Dolby)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#if RTC_ENABLE_MEDIA
+
+#include "h265nalunit.hpp"
+
+#include "impl/internals.hpp"
+
+#include <cmath>
+
+namespace rtc {
+
+H265NalUnitFragment::H265NalUnitFragment(FragmentType type, bool forbiddenBit, uint8_t nuhLayerId,
+                                         uint8_t nuhTempIdPlus1, uint8_t unitType, binary data)
+    : H265NalUnit(data.size() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE) {
+	setForbiddenBit(forbiddenBit);
+	setNuhLayerId(nuhLayerId);
+	setNuhTempIdPlus1(nuhTempIdPlus1);
+	fragmentIndicator()->setUnitType(H265NalUnitFragment::nal_type_fu);
+	setFragmentType(type);
+	setUnitType(unitType);
+	copy(data.begin(), data.end(), begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+}
+
+std::vector<shared_ptr<H265NalUnitFragment>>
+H265NalUnitFragment::fragmentsFrom(shared_ptr<H265NalUnit> nalu, uint16_t maxFragmentSize) {
+	assert(nalu->size() > maxFragmentSize);
+	auto fragments_count = ceil(double(nalu->size()) / maxFragmentSize);
+	maxFragmentSize = uint16_t(int(ceil(nalu->size() / fragments_count)));
+
+	// 3 bytes for FU indicator and FU header
+	maxFragmentSize -= (H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+	auto f = nalu->forbiddenBit();
+	uint8_t nuhLayerId = nalu->nuhLayerId() & 0x3F;        // 6 bits
+	uint8_t nuhTempIdPlus1 = nalu->nuhTempIdPlus1() & 0x7; // 3 bits
+	uint8_t naluType = nalu->unitType() & 0x3F;            // 6 bits
+	auto payload = nalu->payload();
+	vector<shared_ptr<H265NalUnitFragment>> result{};
+	uint64_t offset = 0;
+	while (offset < payload.size()) {
+		vector<byte> fragmentData;
+		FragmentType fragmentType;
+		if (offset == 0) {
+			fragmentType = FragmentType::Start;
+		} else if (offset + maxFragmentSize < payload.size()) {
+			fragmentType = FragmentType::Middle;
+		} else {
+			if (offset + maxFragmentSize > payload.size()) {
+				maxFragmentSize = uint16_t(payload.size() - offset);
+			}
+			fragmentType = FragmentType::End;
+		}
+		fragmentData = {payload.begin() + offset, payload.begin() + offset + maxFragmentSize};
+		auto fragment = std::make_shared<H265NalUnitFragment>(
+		    fragmentType, f, nuhLayerId, nuhTempIdPlus1, naluType, fragmentData);
+		result.push_back(fragment);
+		offset += maxFragmentSize;
+	}
+	return result;
+}
+
+void H265NalUnitFragment::setFragmentType(FragmentType type) {
+	switch (type) {
+	case FragmentType::Start:
+		fragmentHeader()->setStart(true);
+		fragmentHeader()->setEnd(false);
+		break;
+	case FragmentType::End:
+		fragmentHeader()->setStart(false);
+		fragmentHeader()->setEnd(true);
+		break;
+	default:
+		fragmentHeader()->setStart(false);
+		fragmentHeader()->setEnd(false);
+	}
+}
+
+std::vector<shared_ptr<binary>> H265NalUnits::generateFragments(uint16_t maxFragmentSize) {
+	vector<shared_ptr<binary>> result{};
+	for (auto nalu : *this) {
+		if (nalu->size() > maxFragmentSize) {
+			std::vector<shared_ptr<H265NalUnitFragment>> fragments =
+			    H265NalUnitFragment::fragmentsFrom(nalu, maxFragmentSize);
+			result.insert(result.end(), fragments.begin(), fragments.end());
+		} else {
+			result.push_back(nalu);
+		}
+	}
+	return result;
+}
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */

+ 113 - 0
src/h265rtppacketizer.cpp

@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2023 Zita Liao (Dolby)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#if RTC_ENABLE_MEDIA
+
+#include "h265rtppacketizer.hpp"
+
+#include "impl/internals.hpp"
+
+#include <cassert>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace rtc {
+
+shared_ptr<H265NalUnits> H265RtpPacketizer::splitMessage(binary_ptr message) {
+	auto nalus = std::make_shared<H265NalUnits>();
+	if (separator == NalUnit::Separator::Length) {
+		size_t 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<H265NalUnit>(begin, end));
+			index = naluEndIndex;
+		}
+	} else {
+		NalUnitStartSequenceMatch match = NUSM_noMatch;
+		size_t index = 0;
+		while (index < message->size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index++], separator);
+			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+				match = NUSM_noMatch;
+				break;
+			}
+		}
+
+		size_t naluStartIndex = index;
+
+		while (index < message->size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index], separator);
+			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
+				size_t naluEndIndex = index - sequenceLength;
+				match = NUSM_noMatch;
+				auto begin = message->begin() + naluStartIndex;
+				auto end = message->begin() + naluEndIndex + 1;
+				nalus->push_back(std::make_shared<H265NalUnit>(begin, end));
+				naluStartIndex = index + 1;
+			}
+			index++;
+		}
+		auto begin = message->begin() + naluStartIndex;
+		auto end = message->end();
+		nalus->push_back(std::make_shared<H265NalUnit>(begin, end));
+	}
+	return nalus;
+}
+
+H265RtpPacketizer::H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     uint16_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
+      separator(NalUnit::Separator::Length) {}
+
+H265RtpPacketizer::H265RtpPacketizer(NalUnit::Separator separator,
+                                     shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     uint16_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
+      separator(separator) {}
+
+void H265RtpPacketizer::outgoing(message_vector &messages, [[maybe_unused]] const message_callback &send) {
+	message_vector result;
+	for (const auto &message : messages) {
+		auto nalus = splitMessage(message);
+		auto fragments = nalus->generateFragments(maxFragmentSize);
+		if (fragments.size() == 0)
+			continue;
+
+		for (size_t i = 0; i < fragments.size() - 1; i++)
+			result.push_back(packetize(fragments[i], false));
+
+		result.push_back(packetize(fragments[fragments.size() - 1], true));
+	}
+
+	messages.swap(result);
+}
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */

+ 112 - 36
src/impl/certificate.cpp

@@ -100,18 +100,20 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 
 
 Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
 Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
     : mCredentials(gnutls::new_credentials(), gnutls::free_credentials),
     : mCredentials(gnutls::new_credentials(), gnutls::free_credentials),
-      mFingerprint(make_fingerprint(crt)) {
+      mFingerprint(make_fingerprint(crt, CertificateFingerprint::Algorithm::Sha256)) {
 
 
 	gnutls::check(gnutls_certificate_set_x509_key(*mCredentials, &crt, 1, privkey),
 	gnutls::check(gnutls_certificate_set_x509_key(*mCredentials, &crt, 1, privkey),
 	              "Unable to set certificate and key pair in credentials");
 	              "Unable to set certificate and key pair in credentials");
 }
 }
 
 
 Certificate::Certificate(shared_ptr<gnutls_certificate_credentials_t> creds)
 Certificate::Certificate(shared_ptr<gnutls_certificate_credentials_t> creds)
-    : mCredentials(std::move(creds)), mFingerprint(make_fingerprint(*mCredentials)) {}
+    : mCredentials(std::move(creds)),
+      mFingerprint(make_fingerprint(*mCredentials, CertificateFingerprint::Algorithm::Sha256)) {}
 
 
 gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }
 gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }
 
 
-string make_fingerprint(gnutls_certificate_credentials_t credentials) {
+string make_fingerprint(gnutls_certificate_credentials_t credentials,
+                        CertificateFingerprint::Algorithm fingerprintAlgorithm) {
 	auto new_crt_list = [credentials]() -> gnutls_x509_crt_t * {
 	auto new_crt_list = [credentials]() -> gnutls_x509_crt_t * {
 		gnutls_x509_crt_t *crt_list = nullptr;
 		gnutls_x509_crt_t *crt_list = nullptr;
 		unsigned int crt_list_size = 0;
 		unsigned int crt_list_size = 0;
@@ -127,14 +129,37 @@ string make_fingerprint(gnutls_certificate_credentials_t credentials) {
 
 
 	unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(), free_crt_list);
 	unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(), free_crt_list);
 
 
-	return make_fingerprint(*crt_list);
+	return make_fingerprint(*crt_list, fingerprintAlgorithm);
 }
 }
 
 
-string make_fingerprint(gnutls_x509_crt_t crt) {
-	const size_t size = 32;
-	unsigned char buffer[size];
+string make_fingerprint(gnutls_x509_crt_t crt,
+                        CertificateFingerprint::Algorithm fingerprintAlgorithm) {
+	const size_t size = CertificateFingerprint::AlgorithmSize(fingerprintAlgorithm);
+	std::vector<unsigned char> buffer(size);
 	size_t len = size;
 	size_t len = size;
-	gnutls::check(gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA256, buffer, &len),
+
+	gnutls_digest_algorithm_t hashFunc;
+	switch (fingerprintAlgorithm) {
+	case CertificateFingerprint::Algorithm::Sha1:
+		hashFunc = GNUTLS_DIG_SHA1;
+		break;
+	case CertificateFingerprint::Algorithm::Sha224:
+		hashFunc = GNUTLS_DIG_SHA224;
+		break;
+	case CertificateFingerprint::Algorithm::Sha256:
+		hashFunc = GNUTLS_DIG_SHA256;
+		break;
+	case CertificateFingerprint::Algorithm::Sha384:
+		hashFunc = GNUTLS_DIG_SHA384;
+		break;
+	case CertificateFingerprint::Algorithm::Sha512:
+		hashFunc = GNUTLS_DIG_SHA512;
+		break;
+	default:
+		throw std::invalid_argument("Unknown fingerprint algorithm");
+	}
+
+	gnutls::check(gnutls_x509_crt_get_fingerprint(crt, hashFunc, buffer.data(), &len),
 	              "X509 fingerprint error");
 	              "X509 fingerprint error");
 
 
 	std::ostringstream oss;
 	std::ostringstream oss;
@@ -142,23 +167,47 @@ string make_fingerprint(gnutls_x509_crt_t crt) {
 	for (size_t i = 0; i < len; ++i) {
 	for (size_t i = 0; i < len; ++i) {
 		if (i)
 		if (i)
 			oss << std::setw(1) << ':';
 			oss << std::setw(1) << ':';
-		oss << std::setw(2) << unsigned(buffer[i]);
+		oss << std::setw(2) << unsigned(buffer.at(i));
 	}
 	}
 	return oss.str();
 	return oss.str();
 }
 }
 
 
 #elif USE_MBEDTLS
 #elif USE_MBEDTLS
-string make_fingerprint(shared_ptr<mbedtls_x509_crt> crt) {
-	const int size = 32;
-	uint8_t buffer[size];
+string make_fingerprint(mbedtls_x509_crt *crt,
+                        CertificateFingerprint::Algorithm fingerprintAlgorithm) {
+	const int size = CertificateFingerprint::AlgorithmSize(fingerprintAlgorithm);
+	std::vector<unsigned char> buffer(size);
 	std::stringstream fingerprint;
 	std::stringstream fingerprint;
 
 
-	mbedtls::check(
-	    mbedtls_sha256(crt->raw.p, crt->raw.len, reinterpret_cast<unsigned char *>(buffer), 0),
-	    "Failed to generate certificate fingerprint");
+	switch (fingerprintAlgorithm) {
+	case CertificateFingerprint::Algorithm::Sha1:
+		mbedtls::check(mbedtls_sha1(crt->raw.p, crt->raw.len, buffer.data()),
+		               "Failed to generate certificate fingerprint");
+		break;
+	case CertificateFingerprint::Algorithm::Sha224:
+		mbedtls::check(mbedtls_sha256(crt->raw.p, crt->raw.len, buffer.data(), 1),
+		               "Failed to generate certificate fingerprint");
+
+		break;
+	case CertificateFingerprint::Algorithm::Sha256:
+		mbedtls::check(mbedtls_sha256(crt->raw.p, crt->raw.len, buffer.data(), 0),
+		               "Failed to generate certificate fingerprint");
+		break;
+	case CertificateFingerprint::Algorithm::Sha384:
+		mbedtls::check(mbedtls_sha512(crt->raw.p, crt->raw.len, buffer.data(), 1),
+		               "Failed to generate certificate fingerprint");
+		break;
+	case CertificateFingerprint::Algorithm::Sha512:
+		mbedtls::check(mbedtls_sha512(crt->raw.p, crt->raw.len, buffer.data(), 0),
+		               "Failed to generate certificate fingerprint");
+		break;
+	default:
+		throw std::invalid_argument("Unknown fingerprint algorithm");
+	}
 
 
 	for (auto i = 0; i < size; i++) {
 	for (auto i = 0; i < size; i++) {
-		fingerprint << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(buffer[i]);
+		fingerprint << std::setfill('0') << std::setw(2) << std::hex
+		            << static_cast<int>(buffer.at(i));
 		if (i != (size - 1)) {
 		if (i != (size - 1)) {
 			fingerprint << ":";
 			fingerprint << ":";
 		}
 		}
@@ -168,7 +217,8 @@ string make_fingerprint(shared_ptr<mbedtls_x509_crt> crt) {
 }
 }
 
 
 Certificate::Certificate(shared_ptr<mbedtls_x509_crt> crt, shared_ptr<mbedtls_pk_context> pk)
 Certificate::Certificate(shared_ptr<mbedtls_x509_crt> crt, shared_ptr<mbedtls_pk_context> pk)
-    : mCrt(crt), mPk(pk), mFingerprint(make_fingerprint(crt)) {}
+    : mCrt(crt), mPk(pk),
+      mFingerprint(make_fingerprint(crt.get(), CertificateFingerprint::Algorithm::Sha256)) {}
 
 
 Certificate Certificate::FromString(string crt_pem, string key_pem) {
 Certificate Certificate::FromString(string crt_pem, string key_pem) {
 	PLOG_DEBUG << "Importing certificate from PEM string (MbedTLS)";
 	PLOG_DEBUG << "Importing certificate from PEM string (MbedTLS)";
@@ -395,7 +445,9 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 	case CertificateType::Ecdsa: {
 	case CertificateType::Ecdsa: {
 		PLOG_VERBOSE << "Generating ECDSA P-256 key pair";
 		PLOG_VERBOSE << "Generating ECDSA P-256 key pair";
 #if OPENSSL_VERSION_NUMBER >= 0x30000000
 #if OPENSSL_VERSION_NUMBER >= 0x30000000
-		pkey = shared_ptr<EVP_PKEY>(EVP_EC_gen("P-256"), EVP_PKEY_free);
+		pkey = shared_ptr<EVP_PKEY>(EVP_EC_gen("prime256v1"), EVP_PKEY_free);
+		if (!pkey)
+			throw std::runtime_error("Unable to generate ECDSA P-256 key pair");
 #else
 #else
 		pkey = shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
 		pkey = shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
 		unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ecc(
 		unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ecc(
@@ -404,13 +456,11 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 			throw std::runtime_error("Unable to allocate structure for ECDSA P-256 key pair");
 			throw std::runtime_error("Unable to allocate structure for ECDSA P-256 key pair");
 
 
 		EC_KEY_set_asn1_flag(ecc.get(), OPENSSL_EC_NAMED_CURVE); // Set ASN1 OID
 		EC_KEY_set_asn1_flag(ecc.get(), OPENSSL_EC_NAMED_CURVE); // Set ASN1 OID
-		if (!EC_KEY_generate_key(ecc.get()) ||
-		    !EVP_PKEY_assign_EC_KEY(pkey.get(),
-		                            ecc.release())) // the key will be freed when pkey is freed
-#endif
-		if (!pkey)
+		if (!EC_KEY_generate_key(ecc.get()) || !EVP_PKEY_assign_EC_KEY(pkey.get(), ecc.get()))
 			throw std::runtime_error("Unable to generate ECDSA P-256 key pair");
 			throw std::runtime_error("Unable to generate ECDSA P-256 key pair");
 
 
+		ecc.release(); // the key will be freed when pkey is freed
+#endif
 		break;
 		break;
 	}
 	}
 	case CertificateType::Rsa: {
 	case CertificateType::Rsa: {
@@ -418,6 +468,8 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 		const unsigned int bits = 2048;
 		const unsigned int bits = 2048;
 #if OPENSSL_VERSION_NUMBER >= 0x30000000
 #if OPENSSL_VERSION_NUMBER >= 0x30000000
 		pkey = shared_ptr<EVP_PKEY>(EVP_RSA_gen(bits), EVP_PKEY_free);
 		pkey = shared_ptr<EVP_PKEY>(EVP_RSA_gen(bits), EVP_PKEY_free);
+		if (!pkey)
+			throw std::runtime_error("Unable to generate RSA key pair");
 #else
 #else
 		pkey = shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
 		pkey = shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
 		unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
 		unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
@@ -425,15 +477,14 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 		if (!pkey || !rsa || !exponent)
 		if (!pkey || !rsa || !exponent)
 			throw std::runtime_error("Unable to allocate structures for RSA key pair");
 			throw std::runtime_error("Unable to allocate structures for RSA key pair");
 
 
-		const unsigned int e = 65537;               // 2^16 + 1
+		const unsigned int e = 65537; // 2^16 + 1
 		if (!BN_set_word(exponent.get(), e) ||
 		if (!BN_set_word(exponent.get(), e) ||
 		    !RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
 		    !RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
-		    !EVP_PKEY_assign_RSA(pkey.get(),
-		                         rsa.release())) // the key will be freed when pkey is freed
-#endif
-		if (!pkey)
+		    !EVP_PKEY_assign_RSA(pkey.get(), rsa.get()))
 			throw std::runtime_error("Unable to generate RSA key pair");
 			throw std::runtime_error("Unable to generate RSA key pair");
 
 
+		rsa.release(); // the key will be freed when pkey is freed
+#endif
 		break;
 		break;
 	}
 	}
 	default:
 	default:
@@ -464,17 +515,40 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
 }
 }
 
 
 Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey)
 Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey)
-    : mX509(std::move(x509)), mPKey(std::move(pkey)), mFingerprint(make_fingerprint(mX509.get())) {}
+    : mX509(std::move(x509)), mPKey(std::move(pkey)),
+      mFingerprint(make_fingerprint(mX509.get(), CertificateFingerprint::Algorithm::Sha256)) {}
 
 
 std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
 std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
 	return {mX509.get(), mPKey.get()};
 	return {mX509.get(), mPKey.get()};
 }
 }
 
 
-string make_fingerprint(X509 *x509) {
-	const size_t size = 32;
-	unsigned char buffer[size];
-	unsigned int len = size;
-	if (!X509_digest(x509, EVP_sha256(), buffer, &len))
+string make_fingerprint(X509 *x509, CertificateFingerprint::Algorithm fingerprintAlgorithm) {
+	size_t size = CertificateFingerprint::AlgorithmSize(fingerprintAlgorithm);
+	std::vector<unsigned char> buffer(size);
+	auto len = static_cast<unsigned int>(size);
+
+	const EVP_MD *hashFunc;
+	switch (fingerprintAlgorithm) {
+	case CertificateFingerprint::Algorithm::Sha1:
+		hashFunc = EVP_sha1();
+		break;
+	case CertificateFingerprint::Algorithm::Sha224:
+		hashFunc = EVP_sha224();
+		break;
+	case CertificateFingerprint::Algorithm::Sha256:
+		hashFunc = EVP_sha256();
+		break;
+	case CertificateFingerprint::Algorithm::Sha384:
+		hashFunc = EVP_sha384();
+		break;
+	case CertificateFingerprint::Algorithm::Sha512:
+		hashFunc = EVP_sha512();
+		break;
+	default:
+		throw std::invalid_argument("Unknown fingerprint algorithm");
+	}
+
+	if (!X509_digest(x509, hashFunc, buffer.data(), &len))
 		throw std::runtime_error("X509 fingerprint error");
 		throw std::runtime_error("X509 fingerprint error");
 
 
 	std::ostringstream oss;
 	std::ostringstream oss;
@@ -482,7 +556,7 @@ string make_fingerprint(X509 *x509) {
 	for (size_t i = 0; i < len; ++i) {
 	for (size_t i = 0; i < len; ++i) {
 		if (i)
 		if (i)
 			oss << std::setw(1) << ':';
 			oss << std::setw(1) << ':';
-		oss << std::setw(2) << unsigned(buffer[i]);
+		oss << std::setw(2) << unsigned(buffer.at(i));
 	}
 	}
 	return oss.str();
 	return oss.str();
 }
 }
@@ -497,6 +571,8 @@ future_certificate_ptr make_certificate(CertificateType type) {
 	});
 	});
 }
 }
 
 
-string Certificate::fingerprint() const { return mFingerprint; }
+CertificateFingerprint Certificate::fingerprint() const {
+	return CertificateFingerprint{CertificateFingerprint::Algorithm::Sha256, mFingerprint};
+}
 
 
 } // namespace rtc::impl
 } // namespace rtc::impl

+ 6 - 5
src/impl/certificate.hpp

@@ -9,6 +9,7 @@
 #ifndef RTC_IMPL_CERTIFICATE_H
 #ifndef RTC_IMPL_CERTIFICATE_H
 #define RTC_IMPL_CERTIFICATE_H
 #define RTC_IMPL_CERTIFICATE_H
 
 
+#include "description.hpp" // for CertificateFingerprint
 #include "common.hpp"
 #include "common.hpp"
 #include "configuration.hpp" // for CertificateType
 #include "configuration.hpp" // for CertificateType
 #include "init.hpp"
 #include "init.hpp"
@@ -37,7 +38,7 @@ public:
 	std::tuple<X509 *, EVP_PKEY *> credentials() const;
 	std::tuple<X509 *, EVP_PKEY *> credentials() const;
 #endif
 #endif
 
 
-	string fingerprint() const;
+	CertificateFingerprint fingerprint() const;
 
 
 private:
 private:
 	const init_token mInitToken = Init::Instance().token();
 	const init_token mInitToken = Init::Instance().token();
@@ -57,12 +58,12 @@ private:
 };
 };
 
 
 #if USE_GNUTLS
 #if USE_GNUTLS
-string make_fingerprint(gnutls_certificate_credentials_t credentials);
-string make_fingerprint(gnutls_x509_crt_t crt);
+string make_fingerprint(gnutls_certificate_credentials_t credentials, CertificateFingerprint::Algorithm fingerprintAlgorithm);
+string make_fingerprint(gnutls_x509_crt_t crt, CertificateFingerprint::Algorithm fingerprintAlgorithm);
 #elif USE_MBEDTLS
 #elif USE_MBEDTLS
-string make_fingerprint(shared_ptr<mbedtls_x509_crt> crt);
+string make_fingerprint(mbedtls_x509_crt *crt, CertificateFingerprint::Algorithm fingerprintAlgorithm);
 #else
 #else
-string make_fingerprint(X509 *x509);
+string make_fingerprint(X509 *x509, CertificateFingerprint::Algorithm fingerprintAlgorithm);
 #endif
 #endif
 
 
 using certificate_ptr = shared_ptr<Certificate>;
 using certificate_ptr = shared_ptr<Certificate>;

+ 2 - 2
src/impl/channel.hpp

@@ -28,7 +28,7 @@ struct Channel {
 	virtual void triggerAvailable(size_t count);
 	virtual void triggerAvailable(size_t count);
 	virtual void triggerBufferedAmount(size_t amount);
 	virtual void triggerBufferedAmount(size_t amount);
 
 
-	void flushPendingMessages();
+	virtual void flushPendingMessages();
 	void resetOpenCallback();
 	void resetOpenCallback();
 	void resetCallbacks();
 	void resetCallbacks();
 
 
@@ -43,7 +43,7 @@ struct Channel {
 	std::atomic<size_t> bufferedAmount = 0;
 	std::atomic<size_t> bufferedAmount = 0;
 	std::atomic<size_t> bufferedAmountLowThreshold = 0;
 	std::atomic<size_t> bufferedAmountLowThreshold = 0;
 
 
-private:
+protected:
 	std::atomic<bool> mOpenTriggered = false;
 	std::atomic<bool> mOpenTriggered = false;
 };
 };
 
 

+ 68 - 33
src/impl/datachannel.cpp

@@ -12,7 +12,7 @@
 #include "logcounter.hpp"
 #include "logcounter.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
 #include "sctptransport.hpp"
 #include "sctptransport.hpp"
-
+#include "utils.hpp"
 #include "rtc/datachannel.hpp"
 #include "rtc/datachannel.hpp"
 #include "rtc/track.hpp"
 #include "rtc/track.hpp"
 
 
@@ -28,6 +28,9 @@ using std::chrono::milliseconds;
 
 
 namespace rtc::impl {
 namespace rtc::impl {
 
 
+using utils::to_uint16;
+using utils::to_uint32;
+
 // Messages for the DataChannel establishment protocol (RFC 8832)
 // Messages for the DataChannel establishment protocol (RFC 8832)
 // See https://www.rfc-editor.org/rfc/rfc8832.html
 // See https://www.rfc-editor.org/rfc/rfc8832.html
 
 
@@ -74,13 +77,21 @@ bool DataChannel::IsOpenMessage(message_ptr message) {
 DataChannel::DataChannel(weak_ptr<PeerConnection> pc, string label, string protocol,
 DataChannel::DataChannel(weak_ptr<PeerConnection> pc, string label, string protocol,
                          Reliability reliability)
                          Reliability reliability)
     : mPeerConnection(pc), mLabel(std::move(label)), mProtocol(std::move(protocol)),
     : mPeerConnection(pc), mLabel(std::move(label)), mProtocol(std::move(protocol)),
-      mReliability(std::make_shared<Reliability>(std::move(reliability))),
-      mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
+      mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {
+
+	if(reliability.maxPacketLifeTime && reliability.maxRetransmits)
+		throw std::invalid_argument("Both maxPacketLifeTime and maxRetransmits are set");
+
+    mReliability = std::make_shared<Reliability>(std::move(reliability));
+}
 
 
 DataChannel::~DataChannel() {
 DataChannel::~DataChannel() {
 	PLOG_VERBOSE << "Destroying DataChannel";
 	PLOG_VERBOSE << "Destroying DataChannel";
-
-	close();
+	try {
+		close();
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+	}
 }
 }
 
 
 void DataChannel::close() {
 void DataChannel::close() {
@@ -92,20 +103,17 @@ void DataChannel::close() {
 		transport = mSctpTransport.lock();
 		transport = mSctpTransport.lock();
 	}
 	}
 
 
-	if (mIsOpen.exchange(false) && transport && mStream.has_value())
-		transport->closeStream(mStream.value());
+	if (!mIsClosed.exchange(true)) {
+		if (transport && mStream.has_value())
+			transport->closeStream(mStream.value());
 
 
-	if (!mIsClosed.exchange(true))
 		triggerClosed();
 		triggerClosed();
+	}
 
 
 	resetCallbacks();
 	resetCallbacks();
 }
 }
 
 
-void DataChannel::remoteClose() {
-	mIsOpen = false;
-	if (!mIsClosed.exchange(true))
-		triggerClosed();
-}
+void DataChannel::remoteClose() { close(); }
 
 
 optional<message_variant> DataChannel::receive() {
 optional<message_variant> DataChannel::receive() {
 	auto next = mRecvQueue.pop();
 	auto next = mRecvQueue.pop();
@@ -139,13 +147,13 @@ Reliability DataChannel::reliability() const {
 	return *mReliability;
 	return *mReliability;
 }
 }
 
 
-bool DataChannel::isOpen(void) const { return mIsOpen; }
+bool DataChannel::isOpen(void) const { return !mIsClosed && mIsOpen; }
 
 
 bool DataChannel::isClosed(void) const { return mIsClosed; }
 bool DataChannel::isClosed(void) const { return mIsClosed; }
 
 
 size_t DataChannel::maxMessageSize() const {
 size_t DataChannel::maxMessageSize() const {
 	auto pc = mPeerConnection.lock();
 	auto pc = mPeerConnection.lock();
-	return pc ? pc->remoteMaxMessageSize() : DEFAULT_MAX_MESSAGE_SIZE;
+	return pc ? pc->remoteMaxMessageSize() : DEFAULT_REMOTE_MAX_MESSAGE_SIZE;
 }
 }
 
 
 void DataChannel::assignStream(uint16_t stream) {
 void DataChannel::assignStream(uint16_t stream) {
@@ -247,22 +255,35 @@ void OutgoingDataChannel::open(shared_ptr<SctpTransport> transport) {
 
 
 	uint8_t channelType;
 	uint8_t channelType;
 	uint32_t reliabilityParameter;
 	uint32_t reliabilityParameter;
-	switch (mReliability->type) {
-	case Reliability::Type::Rexmit:
+	if (mReliability->maxPacketLifeTime) {
+		channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
+		reliabilityParameter = to_uint32(mReliability->maxPacketLifeTime->count());
+	} else if (mReliability->maxRetransmits) {
 		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
 		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
-		reliabilityParameter = uint32_t(std::max(std::get<int>(mReliability->rexmit), 0));
-		break;
+		reliabilityParameter = to_uint32(*mReliability->maxRetransmits);
+	}
+	// else {
+	//	channelType = CHANNEL_RELIABLE;
+	//	reliabilityParameter = 0;
+	// }
+	// Deprecated
+	else
+		switch (mReliability->typeDeprecated) {
+		case Reliability::Type::Rexmit:
+			channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
+			reliabilityParameter = to_uint32(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;
+		case Reliability::Type::Timed:
+			channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
+			reliabilityParameter = to_uint32(std::get<milliseconds>(mReliability->rexmit).count());
+			break;
 
 
-	default:
-		channelType = CHANNEL_RELIABLE;
-		reliabilityParameter = 0;
-		break;
-	}
+		default:
+			channelType = CHANNEL_RELIABLE;
+			reliabilityParameter = 0;
+			break;
+		}
 
 
 	if (mReliability->unordered)
 	if (mReliability->unordered)
 		channelType |= 0x80;
 		channelType |= 0x80;
@@ -274,8 +295,8 @@ void OutgoingDataChannel::open(shared_ptr<SctpTransport> transport) {
 	open.channelType = channelType;
 	open.channelType = channelType;
 	open.priority = htons(0);
 	open.priority = htons(0);
 	open.reliabilityParameter = htonl(reliabilityParameter);
 	open.reliabilityParameter = htonl(reliabilityParameter);
-	open.labelLength = htons(uint16_t(mLabel.size()));
-	open.protocolLength = htons(uint16_t(mProtocol.size()));
+	open.labelLength = htons(to_uint16(mLabel.size()));
+	open.protocolLength = htons(to_uint16(mProtocol.size()));
 
 
 	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
 	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
 	std::copy(mLabel.begin(), mLabel.end(), end);
 	std::copy(mLabel.begin(), mLabel.end(), end);
@@ -329,17 +350,31 @@ void IncomingDataChannel::processOpenMessage(message_ptr message) {
 	mProtocol.assign(end + open.labelLength, open.protocolLength);
 	mProtocol.assign(end + open.labelLength, open.protocolLength);
 
 
 	mReliability->unordered = (open.channelType & 0x80) != 0;
 	mReliability->unordered = (open.channelType & 0x80) != 0;
+	mReliability->maxPacketLifeTime.reset();
+	mReliability->maxRetransmits.reset();
+	switch (open.channelType & 0x7F) {
+	case CHANNEL_PARTIAL_RELIABLE_REXMIT:
+		mReliability->maxRetransmits.emplace(open.reliabilityParameter);
+		break;
+	case CHANNEL_PARTIAL_RELIABLE_TIMED:
+		mReliability->maxPacketLifeTime.emplace(milliseconds(open.reliabilityParameter));
+		break;
+	default:
+		break;
+	}
+
+	// Deprecated
 	switch (open.channelType & 0x7F) {
 	switch (open.channelType & 0x7F) {
 	case CHANNEL_PARTIAL_RELIABLE_REXMIT:
 	case CHANNEL_PARTIAL_RELIABLE_REXMIT:
-		mReliability->type = Reliability::Type::Rexmit;
+		mReliability->typeDeprecated = Reliability::Type::Rexmit;
 		mReliability->rexmit = int(open.reliabilityParameter);
 		mReliability->rexmit = int(open.reliabilityParameter);
 		break;
 		break;
 	case CHANNEL_PARTIAL_RELIABLE_TIMED:
 	case CHANNEL_PARTIAL_RELIABLE_TIMED:
-		mReliability->type = Reliability::Type::Timed;
+		mReliability->typeDeprecated = Reliability::Type::Timed;
 		mReliability->rexmit = milliseconds(open.reliabilityParameter);
 		mReliability->rexmit = milliseconds(open.reliabilityParameter);
 		break;
 		break;
 	default:
 	default:
-		mReliability->type = Reliability::Type::Reliable;
+		mReliability->typeDeprecated = Reliability::Type::Reliable;
 		mReliability->rexmit = int(0);
 		mReliability->rexmit = int(0);
 	}
 	}
 
 

+ 27 - 9
src/impl/dtlssrtptransport.cpp

@@ -45,12 +45,24 @@ void DtlsSrtpTransport::Init() { srtp_init(); }
 
 
 void DtlsSrtpTransport::Cleanup() { srtp_shutdown(); }
 void DtlsSrtpTransport::Cleanup() { srtp_shutdown(); }
 
 
+bool DtlsSrtpTransport::IsGcmSupported() {
+#if RTC_SYSTEM_SRTP
+	// system libSRTP may not have GCM support
+	srtp_policy_t policy = {};
+	return srtp_crypto_policy_set_from_profile_for_rtp(
+	           &policy.rtp, srtp_profile_aead_aes_256_gcm) == srtp_err_status_ok;
+#else
+	return true;
+#endif
+}
+
 DtlsSrtpTransport::DtlsSrtpTransport(shared_ptr<IceTransport> lower,
 DtlsSrtpTransport::DtlsSrtpTransport(shared_ptr<IceTransport> lower,
                                      shared_ptr<Certificate> certificate, optional<size_t> mtu,
                                      shared_ptr<Certificate> certificate, optional<size_t> mtu,
+                                     CertificateFingerprint::Algorithm fingerprintAlgorithm,
                                      verifier_callback verifierCallback,
                                      verifier_callback verifierCallback,
                                      message_callback srtpRecvCallback,
                                      message_callback srtpRecvCallback,
                                      state_callback stateChangeCallback)
                                      state_callback stateChangeCallback)
-    : DtlsTransport(lower, certificate, mtu, std::move(verifierCallback),
+    : DtlsTransport(lower, certificate, mtu, fingerprintAlgorithm, 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
 
 
@@ -92,7 +104,8 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
 
 
 	// srtp_protect() and srtp_protect_rtcp() assume that they can write SRTP_MAX_TRAILER_LEN (for
 	// srtp_protect() and srtp_protect_rtcp() assume that they can write SRTP_MAX_TRAILER_LEN (for
 	// the authentication tag) into the location in memory immediately following the RTP packet.
 	// the authentication tag) into the location in memory immediately following the RTP packet.
-	message->resize(size + SRTP_MAX_TRAILER_LEN);
+	// Copy instead of resizing so we don't interfere with media handlers keeping references
+	message = make_message(size + SRTP_MAX_TRAILER_LEN, message);
 
 
 	if (IsRtcp(*message)) { // Demultiplex RTCP and RTP using payload type
 	if (IsRtcp(*message)) { // Demultiplex RTCP and RTP using payload type
 		if (srtp_err_status_t err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)) {
 		if (srtp_err_status_t err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)) {
@@ -258,7 +271,7 @@ void DtlsSrtpTransport::postHandshake() {
 
 
 	mbedtls_dtls_srtp_info srtpInfo;
 	mbedtls_dtls_srtp_info srtpInfo;
 	mbedtls_ssl_get_dtls_srtp_negotiation_result(&mSsl, &srtpInfo);
 	mbedtls_ssl_get_dtls_srtp_negotiation_result(&mSsl, &srtpInfo);
-	if (srtpInfo.private_chosen_dtls_srtp_profile != MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80)
+	if (srtpInfo.MBEDTLS_PRIVATE(chosen_dtls_srtp_profile) != MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80)
 		throw std::runtime_error("Failed to get SRTP profile");
 		throw std::runtime_error("Failed to get SRTP profile");
 
 
 	const srtp_profile_t srtpProfile = srtp_profile_aes128_cm_sha1_80;
 	const srtp_profile_t srtpProfile = srtp_profile_aes128_cm_sha1_80;
@@ -326,11 +339,13 @@ void DtlsSrtpTransport::postHandshake() {
 	std::memcpy(mServerSessionKey.data() + keySize, serverSalt, saltSize);
 	std::memcpy(mServerSessionKey.data() + keySize, serverSalt, saltSize);
 
 
 	srtp_policy_t inbound = {};
 	srtp_policy_t inbound = {};
-	srtp_crypto_policy_set_from_profile_for_rtp(&inbound.rtp, srtpProfile);
-	srtp_crypto_policy_set_from_profile_for_rtcp(&inbound.rtcp, srtpProfile);
+	if (srtp_crypto_policy_set_from_profile_for_rtp(&inbound.rtp, srtpProfile))
+		throw std::runtime_error("SRTP profile is not supported");
+	if (srtp_crypto_policy_set_from_profile_for_rtcp(&inbound.rtcp, srtpProfile))
+		throw std::runtime_error("SRTP profile is not supported");
+
 	inbound.ssrc.type = ssrc_any_inbound;
 	inbound.ssrc.type = ssrc_any_inbound;
 	inbound.key = mIsClient ? mServerSessionKey.data() : mClientSessionKey.data();
 	inbound.key = mIsClient ? mServerSessionKey.data() : mClientSessionKey.data();
-
 	inbound.window_size = 1024;
 	inbound.window_size = 1024;
 	inbound.allow_repeat_tx = true;
 	inbound.allow_repeat_tx = true;
 	inbound.next = nullptr;
 	inbound.next = nullptr;
@@ -340,8 +355,11 @@ void DtlsSrtpTransport::postHandshake() {
 		                         to_string(static_cast<int>(err)));
 		                         to_string(static_cast<int>(err)));
 
 
 	srtp_policy_t outbound = {};
 	srtp_policy_t outbound = {};
-	srtp_crypto_policy_set_from_profile_for_rtp(&outbound.rtp, srtpProfile);
-	srtp_crypto_policy_set_from_profile_for_rtcp(&outbound.rtcp, srtpProfile);
+	if (srtp_crypto_policy_set_from_profile_for_rtp(&outbound.rtp, srtpProfile))
+		throw std::runtime_error("SRTP profile is not supported");
+	if (srtp_crypto_policy_set_from_profile_for_rtcp(&outbound.rtcp, srtpProfile))
+		throw std::runtime_error("SRTP profile is not supported");
+
 	outbound.ssrc.type = ssrc_any_outbound;
 	outbound.ssrc.type = ssrc_any_outbound;
 	outbound.key = mIsClient ? mClientSessionKey.data() : mServerSessionKey.data();
 	outbound.key = mIsClient ? mClientSessionKey.data() : mServerSessionKey.data();
 	outbound.window_size = 1024;
 	outbound.window_size = 1024;
@@ -356,7 +374,7 @@ void DtlsSrtpTransport::postHandshake() {
 }
 }
 
 
 #if !USE_GNUTLS && !USE_MBEDTLS
 #if !USE_GNUTLS && !USE_MBEDTLS
-ProfileParams DtlsSrtpTransport::getProfileParamsFromName(string_view name) {
+DtlsSrtpTransport::ProfileParams DtlsSrtpTransport::getProfileParamsFromName(string_view name) {
 	if (name == "SRTP_AES128_CM_SHA1_80")
 	if (name == "SRTP_AES128_CM_SHA1_80")
 		return {srtp_profile_aes128_cm_sha1_80, SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN};
 		return {srtp_profile_aes128_cm_sha1_80, SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN};
 	if (name == "SRTP_AES128_CM_SHA1_32")
 	if (name == "SRTP_AES128_CM_SHA1_32")

+ 10 - 8
src/impl/dtlssrtptransport.hpp

@@ -24,20 +24,16 @@
 
 
 namespace rtc::impl {
 namespace rtc::impl {
 
 
-struct ProfileParams {
-	srtp_profile_t srtpProfile;
-	size_t keySize;
-	size_t saltSize;
-};
-
 class DtlsSrtpTransport final : public DtlsTransport {
 class DtlsSrtpTransport final : public DtlsTransport {
 public:
 public:
 	static void Init();
 	static void Init();
 	static void Cleanup();
 	static void Cleanup();
+	static bool IsGcmSupported();
 
 
 	DtlsSrtpTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
 	DtlsSrtpTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
-	                  optional<size_t> mtu, verifier_callback verifierCallback,
-	                  message_callback srtpRecvCallback, state_callback stateChangeCallback);
+	                  optional<size_t> mtu, CertificateFingerprint::Algorithm fingerprintAlgorithm,
+	                  verifier_callback verifierCallback, message_callback srtpRecvCallback,
+	                  state_callback stateChangeCallback);
 	~DtlsSrtpTransport();
 	~DtlsSrtpTransport();
 
 
 	bool sendMedia(message_ptr message);
 	bool sendMedia(message_ptr message);
@@ -48,6 +44,12 @@ private:
 	void postHandshake() override;
 	void postHandshake() override;
 
 
 #if !USE_GNUTLS && !USE_MBEDTLS
 #if !USE_GNUTLS && !USE_MBEDTLS
+	struct ProfileParams {
+		srtp_profile_t srtpProfile;
+		size_t keySize;
+		size_t saltSize;
+	};
+
 	ProfileParams getProfileParamsFromName(string_view name);
 	ProfileParams getProfileParamsFromName(string_view name);
 #endif
 #endif
 
 

+ 66 - 41
src/impl/dtlstransport.cpp

@@ -7,6 +7,7 @@
  */
  */
 
 
 #include "dtlstransport.hpp"
 #include "dtlstransport.hpp"
+#include "dtlssrtptransport.hpp"
 #include "icetransport.hpp"
 #include "icetransport.hpp"
 #include "internals.hpp"
 #include "internals.hpp"
 #include "threadpool.hpp"
 #include "threadpool.hpp"
@@ -47,10 +48,11 @@ 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,
-                             optional<size_t> mtu, verifier_callback verifierCallback,
-                             state_callback stateChangeCallback)
+                             optional<size_t> mtu,
+                             CertificateFingerprint::Algorithm fingerprintAlgorithm,
+                             verifier_callback verifierCallback, state_callback stateChangeCallback)
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
-      mVerifierCallback(std::move(verifierCallback)),
+      mFingerprintAlgorithm(fingerprintAlgorithm), mVerifierCallback(std::move(verifierCallback)),
       mIsClient(lower->role() == Description::Role::Active) {
       mIsClient(lower->role() == Description::Role::Active) {
 
 
 	PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
 	PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
@@ -294,7 +296,7 @@ int DtlsTransport::CertificateCallback(gnutls_session_t session) {
 			return GNUTLS_E_CERTIFICATE_ERROR;
 			return GNUTLS_E_CERTIFICATE_ERROR;
 		}
 		}
 
 
-		string fingerprint = make_fingerprint(crt);
+		string fingerprint = make_fingerprint(crt, t->mFingerprintAlgorithm);
 		gnutls_x509_crt_deinit(crt);
 		gnutls_x509_crt_deinit(crt);
 
 
 		bool success = t->mVerifierCallback(fingerprint);
 		bool success = t->mVerifierCallback(fingerprint);
@@ -373,10 +375,11 @@ const mbedtls_ssl_srtp_profile srtpSupportedProtectionProfiles[] = {
 };
 };
 
 
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
-                             optional<size_t> mtu, verifier_callback verifierCallback,
-                             state_callback stateChangeCallback)
+                             optional<size_t> mtu,
+                             CertificateFingerprint::Algorithm fingerprintAlgorithm,
+                             verifier_callback verifierCallback, state_callback stateChangeCallback)
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
-      mVerifierCallback(std::move(verifierCallback)),
+      mFingerprintAlgorithm(fingerprintAlgorithm), mVerifierCallback(std::move(verifierCallback)),
       mIsClient(lower->role() == Description::Role::Active) {
       mIsClient(lower->role() == Description::Role::Active) {
 
 
 	PLOG_DEBUG << "Initializing DTLS transport (MbedTLS)";
 	PLOG_DEBUG << "Initializing DTLS transport (MbedTLS)";
@@ -400,6 +403,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
 		               "Failed creating Mbed TLS Context");
 		               "Failed creating Mbed TLS Context");
 
 
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+		mbedtls_ssl_conf_verify(&mConf, DtlsTransport::CertificateCallback, this);
+
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 
 
 		auto [crt, pk] = mCertificate->credentials();
 		auto [crt, pk] = mCertificate->credentials();
@@ -603,6 +608,15 @@ void DtlsTransport::doRecv() {
 	}
 	}
 }
 }
 
 
+int DtlsTransport::CertificateCallback(void *ctx, mbedtls_x509_crt *crt, int /*depth*/,
+                                       uint32_t * /*flags*/) {
+	auto this_ = static_cast<DtlsTransport *>(ctx);
+	string fingerprint = make_fingerprint(crt, this_->mFingerprintAlgorithm);
+	std::transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
+	               [](char c) { return char(std::toupper(c)); });
+	return this_->mVerifierCallback(fingerprint) ? 0 : 1;
+}
+
 void DtlsTransport::ExportKeysCallback(void *ctx, mbedtls_ssl_key_export_type /*type*/,
 void DtlsTransport::ExportKeysCallback(void *ctx, mbedtls_ssl_key_export_type /*type*/,
                                        const unsigned char *secret, size_t secret_len,
                                        const unsigned char *secret, size_t secret_len,
                                        const unsigned char client_random[32],
                                        const unsigned char client_random[32],
@@ -713,10 +727,11 @@ void DtlsTransport::Cleanup() {
 }
 }
 
 
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
-                             optional<size_t> mtu, verifier_callback verifierCallback,
-                             state_callback stateChangeCallback)
+                             optional<size_t> mtu,
+                             CertificateFingerprint::Algorithm fingerprintAlgorithm,
+                             verifier_callback verifierCallback, state_callback stateChangeCallback)
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
     : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
-      mVerifierCallback(std::move(verifierCallback)),
+      mFingerprintAlgorithm(fingerprintAlgorithm), mVerifierCallback(std::move(verifierCallback)),
       mIsClient(lower->role() == Description::Role::Active) {
       mIsClient(lower->role() == Description::Role::Active) {
 	PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
 	PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
 
 
@@ -738,7 +753,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
 
 
 		SSL_CTX_set_min_proto_version(mCtx, DTLS1_VERSION);
 		SSL_CTX_set_min_proto_version(mCtx, DTLS1_VERSION);
 		SSL_CTX_set_read_ahead(mCtx, 1);
 		SSL_CTX_set_read_ahead(mCtx, 1);
-		SSL_CTX_set_quiet_shutdown(mCtx, 0); // sent the dtls close_notify alert
+		SSL_CTX_set_quiet_shutdown(mCtx, 0); // send the close_notify alert
 		SSL_CTX_set_info_callback(mCtx, InfoCallback);
 		SSL_CTX_set_info_callback(mCtx, InfoCallback);
 
 
 		SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
 		SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
@@ -754,7 +769,6 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
 		auto ecdh = unique_ptr<EC_KEY, decltype(&EC_KEY_free)>(
 		auto ecdh = unique_ptr<EC_KEY, decltype(&EC_KEY_free)>(
 		    EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);
 		    EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);
 		SSL_CTX_set_tmp_ecdh(mCtx, ecdh.get());
 		SSL_CTX_set_tmp_ecdh(mCtx, ecdh.get());
-		SSL_CTX_set_options(mCtx, SSL_OP_SINGLE_ECDH_USE);
 #endif
 #endif
 
 
 		auto [x509, pkey] = mCertificate->credentials();
 		auto [x509, pkey] = mCertificate->credentials();
@@ -783,16 +797,23 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
 		SSL_set_bio(mSsl, mInBio, mOutBio);
 		SSL_set_bio(mSsl, mInBio, mOutBio);
 
 
 		// RFC 8827: The DTLS-SRTP protection profile SRTP_AES128_CM_HMAC_SHA1_80 MUST be supported
 		// RFC 8827: The DTLS-SRTP protection profile SRTP_AES128_CM_HMAC_SHA1_80 MUST be supported
-		// See https://www.rfc-editor.org/rfc/rfc8827.html#section-6.5 Warning:
-		// SSL_set_tlsext_use_srtp() returns 0 on success and 1 on error
+		// See https://www.rfc-editor.org/rfc/rfc8827.html#section-6.5
+		// Warning: SSL_set_tlsext_use_srtp() returns 0 on success and 1 on error
+#if RTC_ENABLE_MEDIA
 		// Try to use GCM suite
 		// Try to use GCM suite
-		if (SSL_set_tlsext_use_srtp(
+		if (!DtlsSrtpTransport::IsGcmSupported() ||
+		    SSL_set_tlsext_use_srtp(
 		        mSsl, "SRTP_AEAD_AES_256_GCM:SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80")) {
 		        mSsl, "SRTP_AEAD_AES_256_GCM:SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80")) {
+			PLOG_WARNING << "AES-GCM for SRTP is not supported, falling back to default profile";
 			if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"))
 			if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"))
 				throw std::runtime_error("Failed to set SRTP profile: " +
 				throw std::runtime_error("Failed to set SRTP profile: " +
 				                         openssl::error_string(ERR_get_error()));
 				                         openssl::error_string(ERR_get_error()));
 		}
 		}
-
+#else
+		if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"))
+			throw std::runtime_error("Failed to set SRTP profile: " +
+			                         openssl::error_string(ERR_get_error()));
+#endif
 	} catch (...) {
 	} catch (...) {
 		if (mSsl)
 		if (mSsl)
 			SSL_free(mSsl);
 			SSL_free(mSsl);
@@ -819,6 +840,7 @@ void DtlsTransport::start() {
 	registerIncoming();
 	registerIncoming();
 	changeState(State::Connecting);
 	changeState(State::Connecting);
 
 
+	int ret, err;
 	{
 	{
 		std::lock_guard lock(mSslMutex);
 		std::lock_guard lock(mSslMutex);
 
 
@@ -827,11 +849,12 @@ void DtlsTransport::start() {
 		PLOG_VERBOSE << "DTLS MTU set to " << mtu;
 		PLOG_VERBOSE << "DTLS MTU set to " << mtu;
 
 
 		// Initiate the handshake
 		// Initiate the handshake
-		int ret = SSL_do_handshake(mSsl);
-
-		openssl::check(mSsl, ret, "Handshake initiation failed");
+		ret = SSL_do_handshake(mSsl);
+		err = SSL_get_error(mSsl, ret);
 	}
 	}
 
 
+	openssl::check_error(err, "Handshake failed");
+
 	handleTimeout();
 	handleTimeout();
 }
 }
 
 
@@ -848,11 +871,15 @@ bool DtlsTransport::send(message_ptr message) {
 
 
 	PLOG_VERBOSE << "Send size=" << message->size();
 	PLOG_VERBOSE << "Send size=" << message->size();
 
 
-	std::lock_guard lock(mSslMutex);
-	mCurrentDscp = message->dscp;
-	int ret = SSL_write(mSsl, message->data(), int(message->size()));
+	int ret, err;
+	{
+		std::lock_guard lock(mSslMutex);
+		mCurrentDscp = message->dscp;
+		ret = SSL_write(mSsl, message->data(), int(message->size()));
+		err = SSL_get_error(mSsl, ret);
+	}
 
 
-	if (!openssl::check(mSsl, ret))
+	if (!openssl::check_error(err))
 		return false;
 		return false;
 
 
 	return mOutgoingResult;
 	return mOutgoingResult;
@@ -917,17 +944,14 @@ void DtlsTransport::doRecv() {
 
 
 			if (state() == State::Connecting) {
 			if (state() == State::Connecting) {
 				// Continue the handshake
 				// Continue the handshake
-				bool finished;
+				int ret, err;
 				{
 				{
 					std::lock_guard lock(mSslMutex);
 					std::lock_guard lock(mSslMutex);
-					int ret = SSL_do_handshake(mSsl);
-
-					if (!openssl::check(mSsl, ret, "Handshake failed"))
-						break;
-
-					finished = (SSL_is_init_finished(mSsl) != 0);
+					ret = SSL_do_handshake(mSsl);
+					err = SSL_get_error(mSsl, ret);
 				}
 				}
-				if (finished) {
+
+				if (openssl::check_error(err, "Handshake failed")) {
 					// 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://www.rfc-editor.org/rfc/rfc8261.html#section-5
 					// See https://www.rfc-editor.org/rfc/rfc8261.html#section-5
 					{
 					{
@@ -942,20 +966,26 @@ void DtlsTransport::doRecv() {
 			}
 			}
 
 
 			if (state() == State::Connected) {
 			if (state() == State::Connected) {
-				int ret;
+				int ret, err;
 				{
 				{
 					std::lock_guard lock(mSslMutex);
 					std::lock_guard lock(mSslMutex);
 					ret = SSL_read(mSsl, buffer, bufferSize);
 					ret = SSL_read(mSsl, buffer, bufferSize);
+					err = SSL_get_error(mSsl, ret);
+				}
 
 
-					if (!openssl::check(mSsl, ret))
-						break;
+				if (err == SSL_ERROR_ZERO_RETURN) {
+					PLOG_DEBUG << "TLS connection cleanly closed";
+					break;
 				}
 				}
 
 
-				if (ret > 0)
+				if (openssl::check_error(err))
 					recv(make_message(buffer, buffer + ret));
 					recv(make_message(buffer, buffer + ret));
 			}
 			}
 		}
 		}
 
 
+		std::lock_guard lock(mSslMutex);
+		SSL_shutdown(mSsl);
+
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
 		PLOG_ERROR << "DTLS recv: " << e.what();
 		PLOG_ERROR << "DTLS recv: " << e.what();
 	}
 	}
@@ -968,11 +998,6 @@ void DtlsTransport::doRecv() {
 		PLOG_ERROR << "DTLS handshake failed";
 		PLOG_ERROR << "DTLS handshake failed";
 		changeState(State::Failed);
 		changeState(State::Failed);
 	}
 	}
-
-	{
-		std::lock_guard lock(mSslMutex);
-		SSL_shutdown(mSsl);
-	}
 }
 }
 
 
 void DtlsTransport::handleTimeout() {
 void DtlsTransport::handleTimeout() {
@@ -1011,7 +1036,7 @@ int DtlsTransport::CertificateCallback(int /*preverify_ok*/, X509_STORE_CTX *ctx
 	    static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
 	    static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
 
 
 	X509 *crt = X509_STORE_CTX_get_current_cert(ctx);
 	X509 *crt = X509_STORE_CTX_get_current_cert(ctx);
-	string fingerprint = make_fingerprint(crt);
+	string fingerprint = make_fingerprint(crt, t->mFingerprintAlgorithm);
 
 
 	return t->mVerifierCallback(fingerprint) ? 1 : 0;
 	return t->mVerifierCallback(fingerprint) ? 1 : 0;
 }
 }

+ 3 - 0
src/impl/dtlstransport.hpp

@@ -32,6 +32,7 @@ public:
 	using verifier_callback = std::function<bool(const std::string &fingerprint)>;
 	using verifier_callback = std::function<bool(const std::string &fingerprint)>;
 
 
 	DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate, optional<size_t> mtu,
 	DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate, optional<size_t> mtu,
+	              CertificateFingerprint::Algorithm fingerprintAlgorithm,
 	              verifier_callback verifierCallback, state_callback stateChangeCallback);
 	              verifier_callback verifierCallback, state_callback stateChangeCallback);
 	~DtlsTransport();
 	~DtlsTransport();
 
 
@@ -52,6 +53,7 @@ protected:
 
 
 	const optional<size_t> mMtu;
 	const optional<size_t> mMtu;
 	const certificate_ptr mCertificate;
 	const certificate_ptr mCertificate;
+	CertificateFingerprint::Algorithm mFingerprintAlgorithm;
 	const verifier_callback mVerifierCallback;
 	const verifier_callback mVerifierCallback;
 	const bool mIsClient;
 	const bool mIsClient;
 
 
@@ -85,6 +87,7 @@ protected:
 	char mRandBytes[64];
 	char mRandBytes[64];
 	mbedtls_tls_prf_types mTlsProfile = MBEDTLS_SSL_TLS_PRF_NONE;
 	mbedtls_tls_prf_types mTlsProfile = MBEDTLS_SSL_TLS_PRF_NONE;
 
 
+	static int CertificateCallback(void *ctx, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
 	static int WriteCallback(void *ctx, const unsigned char *buf, size_t len);
 	static int WriteCallback(void *ctx, const unsigned char *buf, size_t len);
 	static int ReadCallback(void *ctx, unsigned char *buf, size_t len);
 	static int ReadCallback(void *ctx, unsigned char *buf, size_t len);
 	static void ExportKeysCallback(void *ctx, mbedtls_ssl_key_export_type type,
 	static void ExportKeysCallback(void *ctx, mbedtls_ssl_key_export_type type,

+ 3 - 2
src/impl/httpproxytransport.cpp

@@ -8,8 +8,8 @@
  */
  */
 
 
 #include "httpproxytransport.hpp"
 #include "httpproxytransport.hpp"
-#include "tcptransport.hpp"
 #include "http.hpp"
 #include "http.hpp"
+#include "tcptransport.hpp"
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
@@ -118,7 +118,8 @@ size_t HttpProxyTransport::parseHttpResponse(std::byte *buffer, size_t size) {
 	status >> protocol >> code;
 	status >> protocol >> code;
 
 
 	if (code != 200)
 	if (code != 200)
-		throw std::runtime_error("Unexpected response code " + to_string(code) + " from HTTP proxy");
+		throw std::runtime_error("Unexpected response code " + to_string(code) +
+		                         " from HTTP proxy");
 
 
 	return length;
 	return length;
 }
 }

+ 8 - 7
src/impl/icetransport.cpp

@@ -12,6 +12,7 @@
 #include "transport.hpp"
 #include "transport.hpp"
 #include "utils.hpp"
 #include "utils.hpp"
 
 
+#include <algorithm>
 #include <iostream>
 #include <iostream>
 #include <random>
 #include <random>
 #include <sstream>
 #include <sstream>
@@ -182,7 +183,7 @@ void IceTransport::setRemoteDescription(const Description &description) {
 	// See https://www.rfc-editor.org/rfc/rfc5763.html#section-5
 	// See https://www.rfc-editor.org/rfc/rfc5763.html#section-5
 	if (description.type() == Description::Type::Answer &&
 	if (description.type() == Description::Type::Answer &&
 	    description.role() == Description::Role::ActPass)
 	    description.role() == Description::Role::ActPass)
-		throw std::logic_error("Illegal role actpass in remote answer description");
+		throw std::invalid_argument("Illegal role actpass in remote answer description");
 
 
 	// RFC 5763: Note that if the answerer uses setup:passive, then the DTLS handshake
 	// RFC 5763: Note that if the answerer uses setup:passive, then the DTLS handshake
 	// will not begin until the answerer is received, which adds additional latency.
 	// will not begin until the answerer is received, which adds additional latency.
@@ -193,12 +194,12 @@ void IceTransport::setRemoteDescription(const Description &description) {
 		                                                        : Description::Role::Active;
 		                                                        : Description::Role::Active;
 
 
 	if (mRole == description.role())
 	if (mRole == description.role())
-		throw std::logic_error("Incompatible roles with remote description");
+		throw std::invalid_argument("Incompatible roles with remote description");
 
 
 	mMid = description.bundleMid();
 	mMid = description.bundleMid();
 	if (juice_set_remote_description(mAgent.get(),
 	if (juice_set_remote_description(mAgent.get(),
 	                                 description.generateApplicationSdp("\r\n").c_str()) < 0)
 	                                 description.generateApplicationSdp("\r\n").c_str()) < 0)
-		throw std::runtime_error("Failed to parse ICE settings from remote SDP");
+		throw std::invalid_argument("Invalid ICE settings from remote SDP");
 }
 }
 
 
 bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
 bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
@@ -407,7 +408,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 
 
 	PLOG_DEBUG << "Initializing ICE transport (libnice)";
 	PLOG_DEBUG << "Initializing ICE transport (libnice)";
 
 
-	if(!MainLoop)
+	if (!MainLoop)
 		throw std::logic_error("Main loop for nice agent is not created");
 		throw std::logic_error("Main loop for nice agent is not created");
 
 
 	// RFC 8445: The nomination process that was referred to as "aggressive nomination" in RFC 5245
 	// RFC 8445: The nomination process that was referred to as "aggressive nomination" in RFC 5245
@@ -641,7 +642,7 @@ void IceTransport::setRemoteDescription(const Description &description) {
 	// See https://www.rfc-editor.org/rfc/rfc5763.html#section-5
 	// See https://www.rfc-editor.org/rfc/rfc5763.html#section-5
 	if (description.type() == Description::Type::Answer &&
 	if (description.type() == Description::Type::Answer &&
 	    description.role() == Description::Role::ActPass)
 	    description.role() == Description::Role::ActPass)
-		throw std::logic_error("Illegal role actpass in remote answer description");
+		throw std::invalid_argument("Illegal role actpass in remote answer description");
 
 
 	// RFC 5763: Note that if the answerer uses setup:passive, then the DTLS handshake
 	// RFC 5763: Note that if the answerer uses setup:passive, then the DTLS handshake
 	// will not begin until the answerer is received, which adds additional latency.
 	// will not begin until the answerer is received, which adds additional latency.
@@ -652,7 +653,7 @@ void IceTransport::setRemoteDescription(const Description &description) {
 		                                                        : Description::Role::Active;
 		                                                        : Description::Role::Active;
 
 
 	if (mRole == description.role())
 	if (mRole == description.role())
-		throw std::logic_error("Incompatible roles with remote description");
+		throw std::invalid_argument("Incompatible roles with remote description");
 
 
 	mMid = description.bundleMid();
 	mMid = description.bundleMid();
 	mTrickleTimeout = !description.ended() ? 30s : 0s;
 	mTrickleTimeout = !description.ended() ? 30s : 0s;
@@ -660,7 +661,7 @@ void IceTransport::setRemoteDescription(const Description &description) {
 	// Warning: libnice expects "\n" as end of line
 	// Warning: libnice expects "\n" as end of line
 	if (nice_agent_parse_remote_sdp(mNiceAgent.get(),
 	if (nice_agent_parse_remote_sdp(mNiceAgent.get(),
 	                                description.generateApplicationSdp("\n").c_str()) < 0)
 	                                description.generateApplicationSdp("\n").c_str()) < 0)
-		throw std::runtime_error("Failed to parse ICE settings from remote SDP");
+		throw std::invalid_argument("Invalid ICE settings from remote SDP");
 }
 }
 
 
 bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
 bool IceTransport::addRemoteCandidate(const Candidate &candidate) {

+ 1 - 1
src/impl/init.cpp

@@ -43,7 +43,7 @@ struct Init::TokenPayload {
 	~TokenPayload() {
 	~TokenPayload() {
 		std::thread t(
 		std::thread t(
 		    [](std::promise<void> promise) {
 		    [](std::promise<void> promise) {
-				utils::this_thread::set_name("RTC cleanup");
+			    utils::this_thread::set_name("RTC cleanup");
 			    try {
 			    try {
 				    Init::Instance().doCleanup();
 				    Init::Instance().doCleanup();
 				    promise.set_value();
 				    promise.set_value();

+ 3 - 1
src/impl/internals.hpp

@@ -39,7 +39,9 @@ const uint16_t MAX_SCTP_STREAMS_COUNT = 1024; // Max number of negotiated SCTP s
                                               // of memory, Chromium historically limits to 1024.
                                               // of memory, Chromium historically limits to 1024.
 
 
 const size_t DEFAULT_LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Default local max message size
 const size_t DEFAULT_LOCAL_MAX_MESSAGE_SIZE = 256 * 1024; // Default local max message size
-const size_t DEFAULT_MAX_MESSAGE_SIZE = 65536; // Remote max message size if not specified in SDP
+const size_t DEFAULT_REMOTE_MAX_MESSAGE_SIZE = 65536;     // Remote max message size if not in SDP
+
+const size_t DEFAULT_WS_MAX_MESSAGE_SIZE = 256 * 1024;   // Default max message size for WebSockets
 
 
 const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
 const size_t RECV_QUEUE_LIMIT = 1024 * 1024; // Max per-channel queue size
 
 

+ 142 - 69
src/impl/peerconnection.cpp

@@ -103,7 +103,7 @@ optional<Description> PeerConnection::remoteDescription() const {
 size_t PeerConnection::remoteMaxMessageSize() const {
 size_t PeerConnection::remoteMaxMessageSize() const {
 	const size_t localMax = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
 	const size_t localMax = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
 
 
-	size_t remoteMax = DEFAULT_MAX_MESSAGE_SIZE;
+	size_t remoteMax = DEFAULT_REMOTE_MAX_MESSAGE_SIZE;
 	std::lock_guard lock(mRemoteDescriptionMutex);
 	std::lock_guard lock(mRemoteDescriptionMutex);
 	if (mRemoteDescription)
 	if (mRemoteDescription)
 		if (auto *application = mRemoteDescription->application())
 		if (auto *application = mRemoteDescription->application())
@@ -152,16 +152,23 @@ shared_ptr<IceTransport> PeerConnection::initIceTransport() {
 				    return;
 				    return;
 			    switch (transportState) {
 			    switch (transportState) {
 			    case IceTransport::State::Connecting:
 			    case IceTransport::State::Connecting:
+				    changeIceState(IceState::Checking);
 				    changeState(State::Connecting);
 				    changeState(State::Connecting);
 				    break;
 				    break;
 			    case IceTransport::State::Connected:
 			    case IceTransport::State::Connected:
+				    changeIceState(IceState::Connected);
 				    initDtlsTransport();
 				    initDtlsTransport();
 				    break;
 				    break;
+			    case IceTransport::State::Completed:
+				    changeIceState(IceState::Completed);
+				    break;
 			    case IceTransport::State::Failed:
 			    case IceTransport::State::Failed:
+				    changeIceState(IceState::Failed);
 				    changeState(State::Failed);
 				    changeState(State::Failed);
 				    mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
 				    mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
 				    break;
 				    break;
 			    case IceTransport::State::Disconnected:
 			    case IceTransport::State::Disconnected:
+				    changeIceState(IceState::Disconnected);
 				    changeState(State::Disconnected);
 				    changeState(State::Disconnected);
 				    mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
 				    mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
 				    break;
 				    break;
@@ -204,6 +211,11 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
 
 
 		PLOG_VERBOSE << "Starting DTLS transport";
 		PLOG_VERBOSE << "Starting DTLS transport";
 
 
+		auto fingerprintAlgorithm = CertificateFingerprint::Algorithm::Sha256;
+		if (auto remote = remoteDescription(); remote && remote->fingerprint()) {
+			fingerprintAlgorithm = remote->fingerprint()->algorithm;
+		}
+
 		auto lower = std::atomic_load(&mIceTransport);
 		auto lower = std::atomic_load(&mIceTransport);
 		if (!lower)
 		if (!lower)
 			throw std::logic_error("No underlying ICE transport for DTLS transport");
 			throw std::logic_error("No underlying ICE transport for DTLS transport");
@@ -247,7 +259,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
 
 
 			// DTLS-SRTP
 			// DTLS-SRTP
 			transport = std::make_shared<DtlsSrtpTransport>(
 			transport = std::make_shared<DtlsSrtpTransport>(
-			    lower, certificate, config.mtu, verifierCallback,
+			    lower, certificate, config.mtu, fingerprintAlgorithm, verifierCallback,
 			    weak_bind(&PeerConnection::forwardMedia, this, _1), dtlsStateChangeCallback);
 			    weak_bind(&PeerConnection::forwardMedia, this, _1), dtlsStateChangeCallback);
 #else
 #else
 			PLOG_WARNING << "Ignoring media support (not compiled with media support)";
 			PLOG_WARNING << "Ignoring media support (not compiled with media support)";
@@ -257,7 +269,8 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
 		if (!transport) {
 		if (!transport) {
 			// DTLS only
 			// DTLS only
 			transport = std::make_shared<DtlsTransport>(lower, certificate, config.mtu,
 			transport = std::make_shared<DtlsTransport>(lower, certificate, config.mtu,
-			                                            verifierCallback, dtlsStateChangeCallback);
+			                                            fingerprintAlgorithm, verifierCallback,
+			                                            dtlsStateChangeCallback);
 		}
 		}
 
 
 		return emplaceTransport(this, &mDtlsTransport, std::move(transport));
 		return emplaceTransport(this, &mDtlsTransport, std::move(transport));
@@ -345,11 +358,14 @@ shared_ptr<SctpTransport> PeerConnection::getSctpTransport() const {
 void PeerConnection::closeTransports() {
 void PeerConnection::closeTransports() {
 	PLOG_VERBOSE << "Closing transports";
 	PLOG_VERBOSE << "Closing transports";
 
 
+	// Change ICE state to sink state Closed
+	changeIceState(IceState::Closed);
+
 	// Change state to sink state Closed
 	// Change state to sink state Closed
 	if (!changeState(State::Closed))
 	if (!changeState(State::Closed))
 		return; // already closed
 		return; // already closed
 
 
-	// Reset intercceptor and callbacks now that state is changed
+	// Reset interceptor and callbacks now that state is changed
 	setMediaHandler(nullptr);
 	setMediaHandler(nullptr);
 	resetCallbacks();
 	resetCallbacks();
 
 
@@ -407,14 +423,16 @@ void PeerConnection::rollbackLocalDescription() {
 
 
 bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
 bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
 	std::lock_guard lock(mRemoteDescriptionMutex);
 	std::lock_guard lock(mRemoteDescriptionMutex);
-	auto expectedFingerprint = mRemoteDescription ? mRemoteDescription->fingerprint() : nullopt;
-	if (expectedFingerprint && *expectedFingerprint == fingerprint) {
+	if (!mRemoteDescription || !mRemoteDescription->fingerprint())
+		return false;
+
+	auto expectedFingerprint = mRemoteDescription->fingerprint()->value;
+	if (expectedFingerprint  == fingerprint) {
 		PLOG_VERBOSE << "Valid fingerprint \"" << fingerprint << "\"";
 		PLOG_VERBOSE << "Valid fingerprint \"" << fingerprint << "\"";
 		return true;
 		return true;
 	}
 	}
 
 
-	PLOG_ERROR << "Invalid fingerprint \"" << fingerprint << "\", expected \""
-	           << expectedFingerprint.value_or("[none]") << "\"";
+	PLOG_ERROR << "Invalid fingerprint \"" << fingerprint << "\", expected \"" << expectedFingerprint << "\"";
 	return false;
 	return false;
 }
 }
 
 
@@ -430,23 +448,28 @@ void PeerConnection::forwardMessage(message_ptr message) {
 		return;
 		return;
 
 
 	const uint16_t stream = uint16_t(message->stream);
 	const uint16_t stream = uint16_t(message->stream);
-	auto channel = findDataChannel(stream);
+	auto [channel, found] = findDataChannel(stream);
 
 
 	if (DataChannel::IsOpenMessage(message)) {
 	if (DataChannel::IsOpenMessage(message)) {
+		if (found) {
+			// The stream is already used, the receiver must close the DataChannel
+			PLOG_WARNING << "Got open message on already used stream " << stream;
+			if (channel && !channel->isClosed())
+				channel->close();
+			else
+				sctpTransport->closeStream(message->stream);
+
+			return;
+		}
+
 		const uint16_t remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
 		const uint16_t remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
 		if (stream % 2 != remoteParity) {
 		if (stream % 2 != remoteParity) {
-			// The odd/even rule is violated, close the DataChannel
+			// The odd/even rule is violated, the receiver must close the DataChannel
 			PLOG_WARNING << "Got open message violating the odd/even rule on stream " << stream;
 			PLOG_WARNING << "Got open message violating the odd/even rule on stream " << stream;
 			sctpTransport->closeStream(message->stream);
 			sctpTransport->closeStream(message->stream);
 			return;
 			return;
 		}
 		}
 
 
-		if (channel && channel->isOpen()) {
-			PLOG_WARNING << "Got open message on stream " << stream
-			             << " for an already open DataChannel, closing it first";
-			channel->close();
-		}
-
 		channel = std::make_shared<IncomingDataChannel>(weak_from_this(), sctpTransport);
 		channel = std::make_shared<IncomingDataChannel>(weak_from_this(), sctpTransport);
 		channel->assignStream(stream);
 		channel->assignStream(stream);
 		channel->openCallback =
 		channel->openCallback =
@@ -454,9 +477,7 @@ void PeerConnection::forwardMessage(message_ptr message) {
 
 
 		std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
 		std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
 		mDataChannels.emplace(stream, channel);
 		mDataChannels.emplace(stream, channel);
-	}
-
-	if (!channel) {
+	} else if (!found) {
 		if (message->type == Message::Reset)
 		if (message->type == Message::Reset)
 			return; // ignore
 			return; // ignore
 
 
@@ -466,22 +487,52 @@ void PeerConnection::forwardMessage(message_ptr message) {
 		return;
 		return;
 	}
 	}
 
 
-	// Forward the message
-	channel->incoming(message);
+	if (message->type == Message::Reset) {
+		// Incoming stream is reset, unregister it
+		removeDataChannel(stream);
+	}
+
+	if (channel) {
+		// Forward the message
+		channel->incoming(message);
+	} else {
+		// DataChannel was destroyed, ignore
+		PLOG_DEBUG << "Ignored message on stream " << stream << ", DataChannel is destroyed";
+	}
 }
 }
 
 
-void PeerConnection::forwardMedia(message_ptr message) {
+void PeerConnection::forwardMedia([[maybe_unused]] message_ptr message) {
+#if RTC_ENABLE_MEDIA
 	if (!message)
 	if (!message)
 		return;
 		return;
 
 
-	auto handler = getMediaHandler();
+	// TODO: outgoing
+	if (auto handler = getMediaHandler()) {
+		message_vector messages{std::move(message)};
 
 
-	if (handler) {
-		message = handler->incoming(message);
-		if (!message)
-			return;
+		handler->incoming(messages, [this](message_ptr message) {
+			auto transport = std::atomic_load(&mDtlsTransport);
+			if (auto srtpTransport = std::dynamic_pointer_cast<DtlsSrtpTransport>(transport))
+				srtpTransport->send(std::move(message));
+		});
+
+		for (auto &m : messages)
+			dispatchMedia(std::move(m));
+
+	} else {
+		dispatchMedia(std::move(message));
 	}
 	}
+#endif
+}
 
 
+void PeerConnection::dispatchMedia([[maybe_unused]] message_ptr message) {
+#if RTC_ENABLE_MEDIA
+	std::shared_lock lock(mTracksMutex); // read-only
+	if (mTrackLines.size()==1) {
+		if (auto track = mTrackLines.front().lock())
+			track->incoming(message);
+		return;
+	}
 	// Browsers like to compound their packets with a random SSRC.
 	// Browsers like to compound their packets with a random SSRC.
 	// we have to do this monstrosity to distribute the report blocks
 	// we have to do this monstrosity to distribute the report blocks
 	if (message->type == Message::Control) {
 	if (message->type == Message::Control) {
@@ -531,7 +582,6 @@ void PeerConnection::forwardMedia(message_ptr message) {
 		}
 		}
 
 
 		if (!ssrcs.empty()) {
 		if (!ssrcs.empty()) {
-			std::shared_lock lock(mTracksMutex); // read-only
 			for (uint32_t ssrc : ssrcs) {
 			for (uint32_t ssrc : ssrcs) {
 				if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
 				if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
 					if (auto track = it->second.lock())
 					if (auto track = it->second.lock())
@@ -544,7 +594,6 @@ void PeerConnection::forwardMedia(message_ptr message) {
 
 
 	uint32_t ssrc = uint32_t(message->stream);
 	uint32_t ssrc = uint32_t(message->stream);
 
 
-	std::shared_lock lock(mTracksMutex); // read-only
 	if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
 	if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
 		if (auto track = it->second.lock())
 		if (auto track = it->second.lock())
 			track->incoming(message);
 			track->incoming(message);
@@ -558,15 +607,16 @@ void PeerConnection::forwardMedia(message_ptr message) {
 		// PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
 		// PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
 		return;
 		return;
 	}
 	}
+#endif
 }
 }
 
 
 void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
 void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
-	if (auto channel = findDataChannel(stream))
+	[[maybe_unused]] auto [channel, found] = findDataChannel(stream);
+	if (channel)
 		channel->triggerBufferedAmount(amount);
 		channel->triggerBufferedAmount(amount);
 }
 }
 
 
 shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(string label, DataChannelInit init) {
 shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(string label, DataChannelInit init) {
-	cleanupDataChannels();
 	std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
 	std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
 
 
 	// If the DataChannel is user-negotiated, do not negotiate it in-band
 	// If the DataChannel is user-negotiated, do not negotiate it in-band
@@ -603,13 +653,17 @@ shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(string label, DataCha
 	return channel;
 	return channel;
 }
 }
 
 
-shared_ptr<DataChannel> PeerConnection::findDataChannel(uint16_t stream) {
+std::pair<shared_ptr<DataChannel>, bool> PeerConnection::findDataChannel(uint16_t stream) {
 	std::shared_lock lock(mDataChannelsMutex); // read-only
 	std::shared_lock lock(mDataChannelsMutex); // read-only
 	if (auto it = mDataChannels.find(stream); it != mDataChannels.end())
 	if (auto it = mDataChannels.find(stream); it != mDataChannels.end())
-		if (auto channel = it->second.lock())
-			return channel;
+		return std::make_pair(it->second.lock(), true);
+	else
+		return std::make_pair(nullptr, false);
+}
 
 
-	return nullptr;
+bool PeerConnection::removeDataChannel(uint16_t stream) {
+	std::unique_lock lock(mDataChannelsMutex); // we are going to erase
+	return mDataChannels.erase(stream) != 0;
 }
 }
 
 
 uint16_t PeerConnection::maxDataChannelStream() const {
 uint16_t PeerConnection::maxDataChannelStream() const {
@@ -640,8 +694,7 @@ void PeerConnection::assignDataChannels() {
 			if (stream > maxStream)
 			if (stream > maxStream)
 				throw std::runtime_error("Too many DataChannels");
 				throw std::runtime_error("Too many DataChannels");
 
 
-			auto it = mDataChannels.find(stream);
-			if (it == mDataChannels.end() || !it->second.lock())
+			if (mDataChannels.find(stream) == mDataChannels.end())
 				break;
 				break;
 
 
 			stream += 2;
 			stream += 2;
@@ -662,13 +715,10 @@ void PeerConnection::iterateDataChannels(
 	{
 	{
 		std::shared_lock lock(mDataChannelsMutex); // read-only
 		std::shared_lock lock(mDataChannelsMutex); // read-only
 		locked.reserve(mDataChannels.size());
 		locked.reserve(mDataChannels.size());
-		auto it = mDataChannels.begin();
-		while (it != mDataChannels.end()) {
+		for(auto it = mDataChannels.begin(); it != mDataChannels.end(); ++it) {
 			auto channel = it->second.lock();
 			auto channel = it->second.lock();
 			if (channel && !channel->isClosed())
 			if (channel && !channel->isClosed())
 				locked.push_back(std::move(channel));
 				locked.push_back(std::move(channel));
-
-			++it;
 		}
 		}
 	}
 	}
 
 
@@ -681,19 +731,6 @@ void PeerConnection::iterateDataChannels(
 	}
 	}
 }
 }
 
 
-void PeerConnection::cleanupDataChannels() {
-	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() {
 void PeerConnection::openDataChannels() {
 	if (auto transport = std::atomic_load(&mSctpTransport))
 	if (auto transport = std::atomic_load(&mSctpTransport))
 		iterateDataChannels([&](shared_ptr<DataChannel> channel) {
 		iterateDataChannels([&](shared_ptr<DataChannel> channel) {
@@ -711,6 +748,8 @@ void PeerConnection::remoteCloseDataChannels() {
 }
 }
 
 
 shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
 shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
+	std::unique_lock lock(mTracksMutex); // we are going to emplace
+
 #if !RTC_ENABLE_MEDIA
 #if !RTC_ENABLE_MEDIA
 	// No media support, mark as removed
 	// No media support, mark as removed
 	PLOG_WARNING << "Tracks are disabled (not compiled with media support)";
 	PLOG_WARNING << "Tracks are disabled (not compiled with media support)";
@@ -719,15 +758,21 @@ shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
 
 
 	shared_ptr<Track> track;
 	shared_ptr<Track> track;
 	if (auto it = mTracks.find(description.mid()); it != mTracks.end())
 	if (auto it = mTracks.find(description.mid()); it != mTracks.end())
-		if (track = it->second.lock(); track)
-			track->setDescription(std::move(description));
+		if (auto t = it->second.lock(); t && !t->isClosed())
+			track = std::move(t);
 
 
-	if (!track) {
+	if (track) {
+		track->setDescription(std::move(description));
+	} else {
 		track = std::make_shared<Track>(weak_from_this(), std::move(description));
 		track = std::make_shared<Track>(weak_from_this(), std::move(description));
 		mTracks.emplace(std::make_pair(track->mid(), track));
 		mTracks.emplace(std::make_pair(track->mid(), track));
 		mTrackLines.emplace_back(track);
 		mTrackLines.emplace_back(track);
 	}
 	}
 
 
+	auto handler = getMediaHandler();
+	if (handler)
+		handler->media(track->description());
+
 	if (track->description().isRemoved())
 	if (track->description().isRemoved())
 		track->close();
 		track->close();
 
 
@@ -735,15 +780,22 @@ shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
 }
 }
 
 
 void PeerConnection::iterateTracks(std::function<void(shared_ptr<Track> track)> func) {
 void PeerConnection::iterateTracks(std::function<void(shared_ptr<Track> track)> func) {
-	std::shared_lock lock(mTracksMutex); // read-only
-	for (auto it = mTrackLines.begin(); it != mTrackLines.end(); ++it) {
-		auto track = it->lock();
-		if (track && !track->isClosed()) {
-			try {
-				func(std::move(track));
-			} catch (const std::exception &e) {
-				PLOG_WARNING << e.what();
-			}
+	std::vector<shared_ptr<Track>> locked;
+	{
+		std::shared_lock lock(mTracksMutex); // read-only
+		locked.reserve(mTrackLines.size());
+		for(auto it = mTrackLines.begin(); it != mTrackLines.end(); ++it) {
+			auto track = it->lock();
+			if (track && !track->isClosed())
+				locked.push_back(std::move(track));
+		}
+	}
+
+	for (auto &track : locked) {
+		try {
+			func(std::move(track));
+		} catch (const std::exception &e) {
+			PLOG_WARNING << e.what();
 		}
 		}
 	}
 	}
 }
 }
@@ -852,7 +904,7 @@ void PeerConnection::processLocalDescription(Description description) {
 				        description.addMedia(std::move(reciprocated));
 				        description.addMedia(std::move(reciprocated));
 			        },
 			        },
 			        [&](Description::Media *remoteMedia) {
 			        [&](Description::Media *remoteMedia) {
-				        std::shared_lock lock(mTracksMutex);
+				        std::unique_lock lock(mTracksMutex); // we may emplace a track
 				        if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end()) {
 				        if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end()) {
 					        // Prefer local description
 					        // Prefer local description
 					        if (auto track = it->second.lock()) {
 					        if (auto track = it->second.lock()) {
@@ -897,6 +949,10 @@ void PeerConnection::processLocalDescription(Description description) {
 				        mTrackLines.emplace_back(track);
 				        mTrackLines.emplace_back(track);
 				        triggerTrack(track); // The user may modify the track description
 				        triggerTrack(track); // The user may modify the track description
 
 
+				        auto handler = getMediaHandler();
+				        if (handler)
+					        handler->media(track->description());
+
 				        if (track->description().isRemoved())
 				        if (track->description().isRemoved())
 					        track->close();
 					        track->close();
 
 
@@ -1078,8 +1134,6 @@ string PeerConnection::localBundleMid() const {
 
 
 void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
 void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	std::unique_lock lock(mMediaHandlerMutex);
 	std::unique_lock lock(mMediaHandlerMutex);
-	if (mMediaHandler)
-		mMediaHandler->onOutgoing(nullptr);
 	mMediaHandler = handler;
 	mMediaHandler = handler;
 }
 }
 
 
@@ -1175,6 +1229,24 @@ bool PeerConnection::changeState(State newState) {
 	return true;
 	return true;
 }
 }
 
 
+bool PeerConnection::changeIceState(IceState newState) {
+	if (iceState.exchange(newState) == newState)
+		return false;
+
+	std::ostringstream s;
+	s << newState;
+	PLOG_INFO << "Changed ICE state to " << s.str();
+
+	if (newState == IceState::Closed) {
+		auto callback = std::move(iceStateChangeCallback); // steal the callback
+		callback(IceState::Closed);                        // call it synchronously
+	} else {
+		mProcessor.enqueue(&PeerConnection::trigger<IceState>, shared_from_this(),
+		                   &iceStateChangeCallback, newState);
+	}
+	return true;
+}
+
 bool PeerConnection::changeGatheringState(GatheringState newState) {
 bool PeerConnection::changeGatheringState(GatheringState newState) {
 	if (gatheringState.exchange(newState) == newState)
 	if (gatheringState.exchange(newState) == newState)
 		return false;
 		return false;
@@ -1207,6 +1279,7 @@ void PeerConnection::resetCallbacks() {
 	localDescriptionCallback = nullptr;
 	localDescriptionCallback = nullptr;
 	localCandidateCallback = nullptr;
 	localCandidateCallback = nullptr;
 	stateChangeCallback = nullptr;
 	stateChangeCallback = nullptr;
+	iceStateChangeCallback = nullptr;
 	gatheringStateChangeCallback = nullptr;
 	gatheringStateChangeCallback = nullptr;
 	signalingStateChangeCallback = nullptr;
 	signalingStateChangeCallback = nullptr;
 	trackCallback = nullptr;
 	trackCallback = nullptr;

+ 7 - 2
src/impl/peerconnection.hpp

@@ -29,6 +29,7 @@ namespace rtc::impl {
 
 
 struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	using State = rtc::PeerConnection::State;
 	using State = rtc::PeerConnection::State;
+	using IceState = rtc::PeerConnection::IceState;
 	using GatheringState = rtc::PeerConnection::GatheringState;
 	using GatheringState = rtc::PeerConnection::GatheringState;
 	using SignalingState = rtc::PeerConnection::SignalingState;
 	using SignalingState = rtc::PeerConnection::SignalingState;
 
 
@@ -58,11 +59,11 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	void forwardBufferedAmount(uint16_t stream, size_t amount);
 	void forwardBufferedAmount(uint16_t stream, size_t amount);
 
 
 	shared_ptr<DataChannel> emplaceDataChannel(string label, DataChannelInit init);
 	shared_ptr<DataChannel> emplaceDataChannel(string label, DataChannelInit init);
-	shared_ptr<DataChannel> findDataChannel(uint16_t stream);
+	std::pair<shared_ptr<DataChannel>, bool> findDataChannel(uint16_t stream);
+	bool removeDataChannel(uint16_t stream);
 	uint16_t maxDataChannelStream() const;
 	uint16_t maxDataChannelStream() const;
 	void assignDataChannels();
 	void assignDataChannels();
 	void iterateDataChannels(std::function<void(shared_ptr<DataChannel> channel)> func);
 	void iterateDataChannels(std::function<void(shared_ptr<DataChannel> channel)> func);
-	void cleanupDataChannels();
 	void openDataChannels();
 	void openDataChannels();
 	void closeDataChannels();
 	void closeDataChannels();
 	void remoteCloseDataChannels();
 	void remoteCloseDataChannels();
@@ -92,6 +93,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	void flushPendingTracks();
 	void flushPendingTracks();
 
 
 	bool changeState(State newState);
 	bool changeState(State newState);
+	bool changeIceState(IceState newState);
 	bool changeGatheringState(GatheringState newState);
 	bool changeGatheringState(GatheringState newState);
 	bool changeSignalingState(SignalingState newState);
 	bool changeSignalingState(SignalingState newState);
 
 
@@ -108,6 +110,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 
 
 	const Configuration config;
 	const Configuration config;
 	std::atomic<State> state = State::New;
 	std::atomic<State> state = State::New;
+	std::atomic<IceState> iceState = IceState::New;
 	std::atomic<GatheringState> gatheringState = GatheringState::New;
 	std::atomic<GatheringState> gatheringState = GatheringState::New;
 	std::atomic<SignalingState> signalingState = SignalingState::Stable;
 	std::atomic<SignalingState> signalingState = SignalingState::Stable;
 	std::atomic<bool> negotiationNeeded = false;
 	std::atomic<bool> negotiationNeeded = false;
@@ -118,11 +121,13 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	synchronized_callback<Description> localDescriptionCallback;
 	synchronized_callback<Description> localDescriptionCallback;
 	synchronized_callback<Candidate> localCandidateCallback;
 	synchronized_callback<Candidate> localCandidateCallback;
 	synchronized_callback<State> stateChangeCallback;
 	synchronized_callback<State> stateChangeCallback;
+	synchronized_callback<IceState> iceStateChangeCallback;
 	synchronized_callback<GatheringState> gatheringStateChangeCallback;
 	synchronized_callback<GatheringState> gatheringStateChangeCallback;
 	synchronized_callback<SignalingState> signalingStateChangeCallback;
 	synchronized_callback<SignalingState> signalingStateChangeCallback;
 	synchronized_callback<shared_ptr<rtc::Track>> trackCallback;
 	synchronized_callback<shared_ptr<rtc::Track>> trackCallback;
 
 
 private:
 private:
+	void dispatchMedia(message_ptr message);
 	void updateTrackSsrcCache(const Description &description);
 	void updateTrackSsrcCache(const Description &description);
 
 
 	const init_token mInitToken = Init::Instance().token();
 	const init_token mInitToken = Init::Instance().token();

+ 4 - 3
src/impl/pollservice.cpp

@@ -105,13 +105,12 @@ void PollService::prepare(std::vector<struct pollfd> &pfds, optional<clock::time
 }
 }
 
 
 void PollService::process(std::vector<struct pollfd> &pfds) {
 void PollService::process(std::vector<struct pollfd> &pfds) {
+	std::unique_lock lock(mMutex);
 	auto it = pfds.begin();
 	auto it = pfds.begin();
 	if (it != pfds.end()) {
 	if (it != pfds.end()) {
-		std::unique_lock lock(mMutex);
 		mInterrupter->process(*it++);
 		mInterrupter->process(*it++);
 	}
 	}
 	while (it != pfds.end()) {
 	while (it != pfds.end()) {
-		std::unique_lock lock(mMutex);
 		socket_t sock = it->fd;
 		socket_t sock = it->fd;
 		auto jt = mSocks->find(sock);
 		auto jt = mSocks->find(sock);
 		if (jt != mSocks->end()) {
 		if (jt != mSocks->end()) {
@@ -119,7 +118,9 @@ void PollService::process(std::vector<struct pollfd> &pfds) {
 				auto &entry = jt->second;
 				auto &entry = jt->second;
 				const auto &params = entry.params;
 				const auto &params = entry.params;
 
 
-				if (it->revents & POLLNVAL || it->revents & POLLERR) {
+				if (it->revents & POLLNVAL || it->revents & POLLERR ||
+				    (it->revents & POLLHUP &&
+				     !(it->events & POLLIN))) { // MacOS sets POLLHUP on connection failure
 					PLOG_VERBOSE << "Poll error event";
 					PLOG_VERBOSE << "Poll error event";
 					auto callback = std::move(params.callback);
 					auto callback = std::move(params.callback);
 					mSocks->erase(sock);
 					mSocks->erase(sock);

+ 64 - 36
src/impl/sctptransport.cpp

@@ -10,6 +10,7 @@
 #include "dtlstransport.hpp"
 #include "dtlstransport.hpp"
 #include "internals.hpp"
 #include "internals.hpp"
 #include "logcounter.hpp"
 #include "logcounter.hpp"
+#include "utils.hpp"
 
 
 #include <algorithm>
 #include <algorithm>
 #include <chrono>
 #include <chrono>
@@ -50,28 +51,11 @@
 using namespace std::chrono_literals;
 using namespace std::chrono_literals;
 using namespace std::chrono;
 using namespace std::chrono;
 
 
-namespace {
-
-template <typename T> uint16_t to_uint16(T i) {
-	if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
-	                  std::numeric_limits<uint16_t>::max())
-		return static_cast<uint16_t>(i);
-	else
-		throw std::invalid_argument("Integer out of range");
-}
-
-template <typename T> uint32_t to_uint32(T i) {
-	if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
-	                  std::numeric_limits<uint32_t>::max())
-		return static_cast<uint32_t>(i);
-	else
-		throw std::invalid_argument("Integer out of range");
-}
-
-} // namespace
-
 namespace rtc::impl {
 namespace rtc::impl {
 
 
+using utils::to_uint16;
+using utils::to_uint32;
+
 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");
 
 
@@ -102,9 +86,11 @@ SctpTransport::InstancesSet *SctpTransport::Instances = new InstancesSet;
 
 
 void SctpTransport::Init() {
 void SctpTransport::Init() {
 	usrsctp_init(0, SctpTransport::WriteCallback, SctpTransport::DebugCallback);
 	usrsctp_init(0, SctpTransport::WriteCallback, SctpTransport::DebugCallback);
-	usrsctp_enable_crc32c_offload();       // We'll compute CRC32 only for outgoing packets
 	usrsctp_sysctl_set_sctp_pr_enable(1);  // Enable Partial Reliability Extension (RFC 3758)
 	usrsctp_sysctl_set_sctp_pr_enable(1);  // Enable Partial Reliability Extension (RFC 3758)
 	usrsctp_sysctl_set_sctp_ecn_enable(0); // Disable Explicit Congestion Notification
 	usrsctp_sysctl_set_sctp_ecn_enable(0); // Disable Explicit Congestion Notification
+#ifndef SCTP_ACCEPT_ZERO_CHECKSUM
+	usrsctp_enable_crc32c_offload(); // We'll compute CRC32 only for outgoing packets
+#endif
 #ifdef SCTP_DEBUG
 #ifdef SCTP_DEBUG
 	usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
 	usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
 #endif
 #endif
@@ -167,8 +153,10 @@ void SctpTransport::Cleanup() {
 SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &config, Ports ports,
 SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &config, Ports ports,
                              message_callback recvCallback, amount_callback bufferedAmountCallback,
                              message_callback recvCallback, amount_callback bufferedAmountCallback,
                              state_callback stateChangeCallback)
                              state_callback stateChangeCallback)
-    : Transport(lower, std::move(stateChangeCallback)), mPorts(std::move(ports)),
-      mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
+    : Transport(lower, std::move(stateChangeCallback)),
+      mMaxMessageSize(config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE)),
+      mPorts(std::move(ports)), mSendQueue(0, message_size_func),
+      mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
 	onRecv(std::move(recvCallback));
 	onRecv(std::move(recvCallback));
 
 
 	PLOG_DEBUG << "Initializing SCTP transport";
 	PLOG_DEBUG << "Initializing SCTP transport";
@@ -282,6 +270,16 @@ SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &c
 		throw std::runtime_error("Could not disable SCTP fragmented interleave, errno=" +
 		throw std::runtime_error("Could not disable SCTP fragmented interleave, errno=" +
 		                         std::to_string(errno));
 		                         std::to_string(errno));
 
 
+#ifdef SCTP_ACCEPT_ZERO_CHECKSUM // not available in usrsctp v0.9.5.0
+	// When using SCTP over DTLS, the data integrity is ensured by DTLS. Therefore, there's no
+	// need to check CRC32c additionally when receiving. See
+	// https://datatracker.ietf.org/doc/html/draft-ietf-tsvwg-sctp-zero-checksum
+	int edmid = SCTP_EDMID_LOWER_LAYER_DTLS;
+	if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ACCEPT_ZERO_CHECKSUM, &edmid, sizeof(edmid)))
+		throw std::runtime_error("Could set socket option SCTP_ACCEPT_ZERO_CHECKSUM, errno=" +
+		                         std::to_string(errno));
+#endif
+
 	int rcvBuf = 0;
 	int rcvBuf = 0;
 	socklen_t rcvBufLen = sizeof(rcvBuf);
 	socklen_t rcvBufLen = sizeof(rcvBuf);
 	if (usrsctp_getsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &rcvBuf, &rcvBufLen))
 	if (usrsctp_getsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &rcvBuf, &rcvBufLen))
@@ -294,8 +292,7 @@ SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &c
 		                         std::to_string(errno));
 		                         std::to_string(errno));
 
 
 	// Ensure the buffer is also large enough to accomodate the largest messages
 	// Ensure the buffer is also large enough to accomodate the largest messages
-	const size_t maxMessageSize = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
-	const int minBuf = int(std::min(maxMessageSize, size_t(std::numeric_limits<int>::max())));
+	const int minBuf = int(std::min(mMaxMessageSize, size_t(std::numeric_limits<int>::max())));
 	rcvBuf = std::max(rcvBuf, minBuf);
 	rcvBuf = std::max(rcvBuf, minBuf);
 	sndBuf = std::max(sndBuf, minBuf);
 	sndBuf = std::max(sndBuf, minBuf);
 
 
@@ -379,6 +376,9 @@ bool SctpTransport::send(message_ptr message) {
 
 
 	PLOG_VERBOSE << "Send size=" << message->size();
 	PLOG_VERBOSE << "Send size=" << message->size();
 
 
+	if (message->size() > mMaxMessageSize)
+		throw std::invalid_argument("Message is too large");
+
 	// Flush the queue, and if nothing is pending, try to send directly
 	// Flush the queue, and if nothing is pending, try to send directly
 	if (trySendQueue() && trySendMessage(message))
 	if (trySendQueue() && trySendMessage(message))
 		return true;
 		return true;
@@ -447,7 +447,7 @@ void SctpTransport::incoming(message_ptr message) {
 		mWrittenCondition.wait(lock, [&]() { return mWrittenOnce || state() == State::Failed; });
 		mWrittenCondition.wait(lock, [&]() { return mWrittenOnce || state() == State::Failed; });
 	}
 	}
 
 
-	if(state() == State::Failed)
+	if (state() == State::Failed)
 		return;
 		return;
 
 
 	if (!message) {
 	if (!message) {
@@ -499,24 +499,31 @@ void SctpTransport::doRecv() {
 			if (flags & MSG_NOTIFICATION) {
 			if (flags & MSG_NOTIFICATION) {
 				// SCTP event notification
 				// SCTP event notification
 				mPartialNotification.insert(mPartialNotification.end(), buffer, buffer + len);
 				mPartialNotification.insert(mPartialNotification.end(), buffer, buffer + len);
+
 				if (flags & MSG_EOR) {
 				if (flags & MSG_EOR) {
 					// Notification is complete, process it
 					// Notification is complete, process it
-					auto notification =
-					    reinterpret_cast<union sctp_notification *>(mPartialNotification.data());
-					processNotification(notification, mPartialNotification.size());
-					mPartialNotification.clear();
+					binary notification;
+					mPartialNotification.swap(notification);
+					auto n = reinterpret_cast<union sctp_notification *>(notification.data());
+					processNotification(n, notification.size());
 				}
 				}
+
 			} else {
 			} else {
 				// SCTP message
 				// SCTP message
 				mPartialMessage.insert(mPartialMessage.end(), buffer, buffer + len);
 				mPartialMessage.insert(mPartialMessage.end(), buffer, buffer + len);
+				if (mPartialMessage.size() > mMaxMessageSize) {
+					PLOG_WARNING << "SCTP message is too large, truncating it";
+					mPartialMessage.resize(mMaxMessageSize);
+				}
+
 				if (flags & MSG_EOR) {
 				if (flags & MSG_EOR) {
 					// Message is complete, process it
 					// Message is complete, process it
+					binary message;
+					mPartialMessage.swap(message);
 					if (infotype != SCTP_RECVV_RCVINFO)
 					if (infotype != SCTP_RECVV_RCVINFO)
 						throw std::runtime_error("Missing SCTP recv info");
 						throw std::runtime_error("Missing SCTP recv info");
 
 
-					processData(std::move(mPartialMessage), info.rcv_sid,
-					            PayloadId(ntohl(info.rcv_ppid)));
-					mPartialMessage.clear();
+					processData(std::move(message), info.rcv_sid, PayloadId(ntohl(info.rcv_ppid)));
 				}
 				}
 			}
 			}
 		}
 		}
@@ -628,7 +635,20 @@ bool SctpTransport::trySendMessage(message_ptr message) {
 	if (reliability.unordered)
 	if (reliability.unordered)
 		spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
 		spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
 
 
-	switch (reliability.type) {
+	if (reliability.maxPacketLifeTime) {
+		spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+		spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
+		spa.sendv_prinfo.pr_value = to_uint32(reliability.maxPacketLifeTime->count());
+	} else if (reliability.maxRetransmits) {
+		spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+		spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
+		spa.sendv_prinfo.pr_value = to_uint32(*reliability.maxRetransmits);
+	}
+	// else {
+	// 	spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_NONE;
+	// }
+	// Deprecated
+	else switch (reliability.typeDeprecated) {
 	case Reliability::Type::Rexmit:
 	case Reliability::Type::Rexmit:
 		spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
 		spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
 		spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
 		spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
@@ -773,6 +793,7 @@ void SctpTransport::processData(binary &&data, uint16_t sid, PayloadId ppid) {
 
 
 	case PPID_STRING_PARTIAL: // deprecated
 	case PPID_STRING_PARTIAL: // deprecated
 		mPartialStringData.insert(mPartialStringData.end(), data.begin(), data.end());
 		mPartialStringData.insert(mPartialStringData.end(), data.begin(), data.end());
+		mPartialStringData.resize(mMaxMessageSize);
 		break;
 		break;
 
 
 	case PPID_STRING:
 	case PPID_STRING:
@@ -781,9 +802,11 @@ void SctpTransport::processData(binary &&data, uint16_t sid, PayloadId ppid) {
 			recv(make_message(std::move(data), Message::String, sid));
 			recv(make_message(std::move(data), Message::String, sid));
 		} else {
 		} else {
 			mPartialStringData.insert(mPartialStringData.end(), data.begin(), data.end());
 			mPartialStringData.insert(mPartialStringData.end(), data.begin(), data.end());
+			mPartialStringData.resize(mMaxMessageSize);
 			mBytesReceived += mPartialStringData.size();
 			mBytesReceived += mPartialStringData.size();
-			recv(make_message(std::move(mPartialStringData), Message::String, sid));
+			auto message = make_message(std::move(mPartialStringData), Message::String, sid);
 			mPartialStringData.clear();
 			mPartialStringData.clear();
+			recv(std::move(message));
 		}
 		}
 		break;
 		break;
 
 
@@ -794,6 +817,7 @@ void SctpTransport::processData(binary &&data, uint16_t sid, PayloadId ppid) {
 
 
 	case PPID_BINARY_PARTIAL: // deprecated
 	case PPID_BINARY_PARTIAL: // deprecated
 		mPartialBinaryData.insert(mPartialBinaryData.end(), data.begin(), data.end());
 		mPartialBinaryData.insert(mPartialBinaryData.end(), data.begin(), data.end());
+		mPartialBinaryData.resize(mMaxMessageSize);
 		break;
 		break;
 
 
 	case PPID_BINARY:
 	case PPID_BINARY:
@@ -802,9 +826,11 @@ void SctpTransport::processData(binary &&data, uint16_t sid, PayloadId ppid) {
 			recv(make_message(std::move(data), Message::Binary, sid));
 			recv(make_message(std::move(data), Message::Binary, sid));
 		} else {
 		} else {
 			mPartialBinaryData.insert(mPartialBinaryData.end(), data.begin(), data.end());
 			mPartialBinaryData.insert(mPartialBinaryData.end(), data.begin(), data.end());
+			mPartialBinaryData.resize(mMaxMessageSize);
 			mBytesReceived += mPartialBinaryData.size();
 			mBytesReceived += mPartialBinaryData.size();
-			recv(make_message(std::move(mPartialBinaryData), Message::Binary, sid));
+			auto message = make_message(std::move(mPartialBinaryData), Message::Binary, sid);
 			mPartialBinaryData.clear();
 			mPartialBinaryData.clear();
+			recv(std::move(message));
 		}
 		}
 		break;
 		break;
 
 
@@ -943,12 +969,14 @@ void SctpTransport::UpcallCallback(struct socket *, void *arg, int /* flags */)
 int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
 int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
 	auto *transport = static_cast<SctpTransport *>(ptr);
 	auto *transport = static_cast<SctpTransport *>(ptr);
 
 
+#ifndef SCTP_ACCEPT_ZERO_CHECKSUM
 	// Set the CRC32 ourselves as we have enabled CRC32 offloading
 	// Set the CRC32 ourselves as we have enabled CRC32 offloading
 	if (len >= 12) {
 	if (len >= 12) {
 		uint32_t *checksum = reinterpret_cast<uint32_t *>(data) + 2;
 		uint32_t *checksum = reinterpret_cast<uint32_t *>(data) + 2;
 		*checksum = 0;
 		*checksum = 0;
 		*checksum = usrsctp_crc32c(data, len);
 		*checksum = usrsctp_crc32c(data, len);
 	}
 	}
+#endif
 
 
 	// Workaround for sctplab/usrsctp#405: Send callback is invoked on already closed socket
 	// Workaround for sctplab/usrsctp#405: Send callback is invoked on already closed socket
 	// https://github.com/sctplab/usrsctp/issues/405
 	// https://github.com/sctplab/usrsctp/issues/405

+ 1 - 0
src/impl/sctptransport.hpp

@@ -96,6 +96,7 @@ private:
 	void processData(binary &&data, uint16_t streamId, PayloadId ppid);
 	void processData(binary &&data, uint16_t streamId, PayloadId ppid);
 	void processNotification(const union sctp_notification *notify, size_t len);
 	void processNotification(const union sctp_notification *notify, size_t len);
 
 
+	const size_t mMaxMessageSize;
 	const Ports mPorts;
 	const Ports mPorts;
 	struct socket *mSock;
 	struct socket *mSock;
 	std::optional<uint16_t> mNegotiatedStreamsCount;
 	std::optional<uint16_t> mNegotiatedStreamsCount;

+ 2 - 2
src/impl/tcpserver.cpp

@@ -21,7 +21,7 @@
 
 
 namespace rtc::impl {
 namespace rtc::impl {
 
 
-TcpServer::TcpServer(uint16_t port, const char* bindAddress) {
+TcpServer::TcpServer(uint16_t port, const char *bindAddress) {
 	PLOG_DEBUG << "Initializing TCP server";
 	PLOG_DEBUG << "Initializing TCP server";
 	listen(port, bindAddress);
 	listen(port, bindAddress);
 }
 }
@@ -89,7 +89,7 @@ void TcpServer::close() {
 	}
 	}
 }
 }
 
 
-void TcpServer::listen(uint16_t port, const char* bindAddress) {
+void TcpServer::listen(uint16_t port, const char *bindAddress) {
 	PLOG_DEBUG << "Listening on port " << port;
 	PLOG_DEBUG << "Listening on port " << port;
 
 
 	struct addrinfo hints = {};
 	struct addrinfo hints = {};

+ 2 - 2
src/impl/tcpserver.hpp

@@ -21,7 +21,7 @@ namespace rtc::impl {
 
 
 class TcpServer final {
 class TcpServer final {
 public:
 public:
-	TcpServer(uint16_t port, const char* bindAddress = nullptr);
+	TcpServer(uint16_t port, const char *bindAddress = nullptr);
 	~TcpServer();
 	~TcpServer();
 
 
 	TcpServer(const TcpServer &other) = delete;
 	TcpServer(const TcpServer &other) = delete;
@@ -33,7 +33,7 @@ public:
 	uint16_t port() const { return mPort; }
 	uint16_t port() const { return mPort; }
 
 
 private:
 private:
-	void listen(uint16_t port, const char* bindAddress);
+	void listen(uint16_t port, const char *bindAddress);
 
 
 	uint16_t mPort;
 	uint16_t mPort;
 	socket_t mSock = INVALID_SOCKET;
 	socket_t mSock = INVALID_SOCKET;

+ 27 - 3
src/impl/tcptransport.cpp

@@ -26,6 +26,30 @@ using namespace std::chrono_literals;
 using std::chrono::duration_cast;
 using std::chrono::duration_cast;
 using std::chrono::milliseconds;
 using std::chrono::milliseconds;
 
 
+namespace {
+
+bool unmap_inet6_v4mapped(struct sockaddr *sa, socklen_t *len) {
+	if (sa->sa_family != AF_INET6)
+		return false;
+
+	const auto *sin6 = reinterpret_cast<struct sockaddr_in6 *>(sa);
+	if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+		return false;
+
+	struct sockaddr_in6 copy = *sin6;
+	sin6 = &copy;
+
+	auto *sin = reinterpret_cast<struct sockaddr_in *>(sa);
+	std::memset(sin, 0, sizeof(*sin));
+	sin->sin_family = AF_INET;
+	sin->sin_port = sin6->sin6_port;
+	std::memcpy(&sin->sin_addr, reinterpret_cast<const uint8_t *>(&sin6->sin6_addr) + 12, 4);
+	*len = sizeof(*sin);
+	return true;
+}
+
+}
+
 TcpTransport::TcpTransport(string hostname, string service, state_callback callback)
 TcpTransport::TcpTransport(string hostname, string service, state_callback callback)
     : Transport(nullptr, std::move(callback)), mIsActive(true), mHostname(std::move(hostname)),
     : Transport(nullptr, std::move(callback)), mIsActive(true), mHostname(std::move(hostname)),
       mService(std::move(service)), mSock(INVALID_SOCKET) {
       mService(std::move(service)), mSock(INVALID_SOCKET) {
@@ -47,6 +71,8 @@ TcpTransport::TcpTransport(socket_t sock, state_callback callback)
 	if (::getpeername(mSock, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0)
 	if (::getpeername(mSock, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0)
 		throw std::runtime_error("getsockname failed");
 		throw std::runtime_error("getsockname failed");
 
 
+	unmap_inet6_v4mapped(reinterpret_cast<struct sockaddr *>(&addr), &addrlen);
+
 	char node[MAX_NUMERICNODE_LEN];
 	char node[MAX_NUMERICNODE_LEN];
 	char serv[MAX_NUMERICSERV_LEN];
 	char serv[MAX_NUMERICSERV_LEN];
 	if (::getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), addrlen, node,
 	if (::getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), addrlen, node,
@@ -58,9 +84,7 @@ TcpTransport::TcpTransport(socket_t sock, state_callback callback)
 	mService = serv;
 	mService = serv;
 }
 }
 
 
-TcpTransport::~TcpTransport() {
-	close();
-}
+TcpTransport::~TcpTransport() { close(); }
 
 
 void TcpTransport::onBufferedAmount(amount_callback callback) {
 void TcpTransport::onBufferedAmount(amount_callback callback) {
 	mBufferedAmountCallback = std::move(callback);
 	mBufferedAmountCallback = std::move(callback);

+ 26 - 15
src/impl/tls.cpp

@@ -163,32 +163,43 @@ void init() {
 	}
 	}
 }
 }
 
 
-string error_string(unsigned long err) {
+string error_string(unsigned long error) {
 	const size_t bufferSize = 256;
 	const size_t bufferSize = 256;
 	char buffer[bufferSize];
 	char buffer[bufferSize];
-	ERR_error_string_n(err, buffer, bufferSize);
+	ERR_error_string_n(error, buffer, bufferSize);
 	return string(buffer);
 	return string(buffer);
 }
 }
 
 
 bool check(int success, const string &message) {
 bool check(int success, const string &message) {
-	if (success)
+	unsigned long last_error = ERR_peek_last_error();
+	ERR_clear_error();
+
+	if (success > 0)
 		return true;
 		return true;
 
 
-	string str = error_string(ERR_get_error());
-	throw std::runtime_error(message + ": " + str);
+	throw std::runtime_error(message + (last_error != 0 ? ": " + error_string(last_error) : ""));
 }
 }
 
 
-// Return false on EOF
-bool check(SSL *ssl, int ret, const string &message) {
-	unsigned long err = SSL_get_error(ssl, ret);
-	if (err == SSL_ERROR_NONE || err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+// Return false on recoverable error
+bool check_error(int err, const string &message) {
+	unsigned long last_error = ERR_peek_last_error();
+	ERR_clear_error();
+
+	if (err == SSL_ERROR_NONE)
 		return true;
 		return true;
-	}
-	if (err == SSL_ERROR_ZERO_RETURN) {
-		return false;
-	}
-	string str = error_string(err);
-	throw std::runtime_error(message + ": " + str);
+
+	if (err == SSL_ERROR_ZERO_RETURN)
+		throw std::runtime_error(message + ": peer closed connection");
+
+	if (err == SSL_ERROR_SYSCALL)
+		throw std::runtime_error(message + ": fatal I/O error");
+
+	if (err == SSL_ERROR_SSL)
+		throw std::runtime_error(message +
+		                         (last_error != 0 ? ": " + error_string(last_error) : ""));
+
+	// SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE end up here
+	return false;
 }
 }
 
 
 BIO *BIO_new_from_file(const string &filename) {
 BIO *BIO_new_from_file(const string &filename) {

+ 2 - 2
src/impl/tls.hpp

@@ -82,10 +82,10 @@ std::shared_ptr<mbedtls_x509_crt> new_x509_crt();
 namespace rtc::openssl {
 namespace rtc::openssl {
 
 
 void init();
 void init();
-string error_string(unsigned long err);
+string error_string(unsigned long error);
 
 
 bool check(int success, const string &message = "OpenSSL error");
 bool check(int success, const string &message = "OpenSSL error");
-bool check(SSL *ssl, int ret, const string &message = "OpenSSL error");
+bool check_error(int err, const string &message = "OpenSSL error");
 
 
 BIO *BIO_new_from_file(const string &filename);
 BIO *BIO_new_from_file(const string &filename);
 
 

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