Browse Source

Merge branch 'master' into fix-uwp

Paul-Louis Ageneau 4 years ago
parent
commit
4a526f66b6
62 changed files with 1835 additions and 946 deletions
  1. 2 2
      .github/workflows/build-gnutls.yml
  2. 15 1
      .github/workflows/build-nice.yml
  3. 3 2
      .github/workflows/build-openssl.yml
  4. 3 0
      .gitmodules
  5. 28 27
      CMakeLists.txt
  6. 27 3
      README.md
  7. 1 1
      deps/libjuice
  8. 1 0
      deps/libsrtp
  9. 4 0
      examples/client/getopt.cpp
  10. 2 2
      examples/media/main.cpp
  11. 15 0
      examples/sfu-media/CMakeLists.txt
  12. 134 0
      examples/sfu-media/main.cpp
  13. 87 0
      examples/sfu-media/main.html
  14. 1 1
      examples/web/script.js
  15. 6 3
      include/rtc/candidate.hpp
  16. 2 2
      include/rtc/channel.hpp
  17. 27 11
      include/rtc/datachannel.hpp
  18. 41 16
      include/rtc/description.hpp
  19. 1 1
      include/rtc/log.hpp
  20. 17 9
      include/rtc/peerconnection.hpp
  21. 0 1
      include/rtc/queue.hpp
  22. 0 1
      include/rtc/reliability.hpp
  23. 35 21
      include/rtc/rtc.h
  24. 0 1
      include/rtc/rtc.hpp
  25. 43 11
      include/rtc/rtcp.hpp
  26. 493 0
      include/rtc/rtp.hpp
  27. 4 1
      include/rtc/track.hpp
  28. 1 0
      include/rtc/websocket.hpp
  29. 1 2
      src/base64.cpp
  30. 29 24
      src/candidate.cpp
  31. 127 178
      src/capi.cpp
  32. 11 10
      src/certificate.cpp
  33. 3 10
      src/channel.cpp
  34. 89 56
      src/datachannel.cpp
  35. 160 48
      src/description.cpp
  36. 80 26
      src/dtlssrtptransport.cpp
  37. 11 1
      src/dtlssrtptransport.hpp
  38. 2 3
      src/dtlstransport.cpp
  39. 0 1
      src/dtlstransport.hpp
  40. 10 8
      src/icetransport.cpp
  41. 1 1
      src/icetransport.hpp
  42. 0 1
      src/init.cpp
  43. 1 2
      src/log.cpp
  44. 157 68
      src/peerconnection.cpp
  45. 0 1
      src/processor.cpp
  46. 1 2
      src/processor.hpp
  47. 35 324
      src/rtcp.cpp
  48. 3 3
      src/sctptransport.cpp
  49. 1 1
      src/sctptransport.hpp
  50. 0 1
      src/threadpool.cpp
  51. 7 7
      src/threadpool.hpp
  52. 0 1
      src/tls.cpp
  53. 2 2
      src/tls.hpp
  54. 8 9
      src/tlstransport.cpp
  55. 41 19
      src/track.cpp
  56. 7 5
      src/transport.hpp
  57. 0 2
      src/verifiedtlstransport.cpp
  58. 16 4
      src/websocket.cpp
  59. 1 1
      src/wstransport.cpp
  60. 33 2
      test/connectivity.cpp
  61. 1 1
      test/track.cpp
  62. 4 5
      test/websocket.cpp

+ 2 - 2
.github/workflows/build-gnutls.yml

@@ -12,11 +12,11 @@ jobs:
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
     - name: install packages
     - name: install packages
-      run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
+      run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev libsrtp2-dev
     - name: submodules
     - name: submodules
       run: git submodule update --init --recursive
       run: git submodule update --init --recursive
     - name: cmake
     - name: cmake
-      run: cmake -B build -DUSE_GNUTLS=1 -DWARNINGS_AS_ERRORS=1
+      run: cmake -B build -DUSE_GNUTLS=1 -DUSE_SYSTEM_SRTP=1 -DWARNINGS_AS_ERRORS=1
     - name: make
     - name: make
       run: (cd build; make -j2)
       run: (cd build; make -j2)
     - name: test
     - name: test

+ 15 - 1
.github/workflows/build-nice.yml

@@ -7,7 +7,7 @@ on:
     branches:
     branches:
     - master
     - master
 jobs:
 jobs:
-  build-linux:
+  build-media:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -21,4 +21,18 @@ jobs:
       run: (cd build; make -j2)
       run: (cd build; make -j2)
     - name: test
     - name: test
       run: ./build/tests
       run: ./build/tests
+  build-no-media:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: install packages
+      run: sudo apt update && sudo apt install libgnutls28-dev libnice-dev
+    - name: submodules
+      run: git submodule update --init --recursive
+    - name: cmake
+      run: cmake -B build -DUSE_GNUTLS=1 -DUSE_NICE=1 -DNO_MEDIA=1 -DWARNINGS_AS_ERRORS=1
+    - name: make
+      run: (cd build; make -j2)
+    - name: test
+      run: ./build/tests
 
 

+ 3 - 2
.github/workflows/build-openssl.yml

@@ -12,11 +12,11 @@ jobs:
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
     - name: install packages
     - name: install packages
-      run: sudo apt update && sudo apt install libssl-dev
+      run: sudo apt update && sudo apt install libssl-dev libsrtp2-dev
     - name: submodules
     - name: submodules
       run: git submodule update --init --recursive
       run: git submodule update --init --recursive
     - name: cmake
     - name: cmake
-      run: cmake -B build -DUSE_GNUTLS=0 -DWARNINGS_AS_ERRORS=1
+      run: cmake -B build -DUSE_GNUTLS=0 -DUSE_SYSTEM_SRTP=1 -DWARNINGS_AS_ERRORS=1
     - name: make
     - name: make
       run: (cd build; make -j2)
       run: (cd build; make -j2)
     - name: test
     - name: test
@@ -52,6 +52,7 @@ jobs:
     - name: nmake
     - name: nmake
       run: |
       run: |
         cd build
         cd build
+        set CL=/MP
         nmake
         nmake
     - name: test
     - name: test
       run: build/tests.exe
       run: build/tests.exe

+ 3 - 0
.gitmodules

@@ -10,3 +10,6 @@
 [submodule "deps/json"]
 [submodule "deps/json"]
 	path = deps/json
 	path = deps/json
 	url = https://github.com/nlohmann/json.git
 	url = https://github.com/nlohmann/json.git
+[submodule "deps/libsrtp"]
+	path = deps/libsrtp
+	url = https://github.com/cisco/libsrtp.git

+ 28 - 27
CMakeLists.txt

@@ -7,15 +7,14 @@ set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
 # Options
 # Options
 option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
 option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
 option(USE_NICE "Use libnice instead of libjuice" OFF)
 option(USE_NICE "Use libnice instead of libjuice" OFF)
+option(USE_SYSTEM_SRTP "Use system libSRTP" OFF)
 option(NO_WEBSOCKET "Disable WebSocket support" OFF)
 option(NO_WEBSOCKET "Disable WebSocket support" OFF)
+option(NO_MEDIA "Disable media transport support" OFF)
 option(NO_EXAMPLES "Disable examples" OFF)
 option(NO_EXAMPLES "Disable examples" OFF)
 option(NO_TESTS "Disable tests build" OFF)
 option(NO_TESTS "Disable tests build" OFF)
 option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
 option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
 option(RSA_KEY_BITS_2048 "Use 2048-bit RSA key instead of 3072-bit" OFF)
 option(RSA_KEY_BITS_2048 "Use 2048-bit RSA key instead of 3072-bit" 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 USE_SRTP defaults to AUTO (enabled if libSRTP is found, else disabled)
-set(USE_SRTP AUTO CACHE STRING "Use libSRTP and enable media support")
-set_property(CACHE USE_SRTP PROPERTY STRINGS AUTO ON OFF)
 
 
 if(USE_NICE)
 if(USE_NICE)
 	option(USE_JUICE "Use libjuice" OFF)
 	option(USE_JUICE "Use libjuice" OFF)
@@ -90,6 +89,7 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtp.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
 )
 )
@@ -166,32 +166,32 @@ if(WIN32)
 	target_link_libraries(datachannel-static PRIVATE ws2_32) # winsock2
 	target_link_libraries(datachannel-static PRIVATE ws2_32) # winsock2
 endif()
 endif()
 
 
-if(USE_SRTP STREQUAL "AUTO")
-	find_package(SRTP)
-	if(SRTP_FOUND)
-		message(STATUS "LibSRTP found, compiling with media transport")
-	else()
-		message(STATUS "LibSRTP NOT found, compiling WITHOUT media transport")
-	endif()
-elseif(USE_SRTP)
-	find_package(SRTP REQUIRED)
-endif()
-
-if(USE_SRTP AND SRTP_FOUND)
-	if(NOT TARGET SRTP::SRTP)
-		add_library(SRTP::SRTP UNKNOWN IMPORTED)
-		set_target_properties(SRTP::SRTP PROPERTIES
-			INTERFACE_INCLUDE_DIRECTORIES ${SRTP_INCLUDE_DIRS}
-			IMPORTED_LINK_INTERFACE_LANGUAGES C
-			IMPORTED_LOCATION ${SRTP_LIBRARIES})
-	endif()
-	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=1)
-	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=1)
-	target_link_libraries(datachannel PRIVATE SRTP::SRTP)
-	target_link_libraries(datachannel-static PRIVATE SRTP::SRTP)
-else()
+if(NO_MEDIA)
 	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=0)
 	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=0)
+else()
+	target_compile_definitions(datachannel PUBLIC RTC_ENABLE_MEDIA=1)
+	target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_MEDIA=1)
+	if(USE_SYSTEM_SRTP)
+		find_package(SRTP REQUIRED)
+		if(NOT TARGET SRTP::SRTP)
+			add_library(SRTP::SRTP UNKNOWN IMPORTED)
+			set_target_properties(SRTP::SRTP PROPERTIES
+				INTERFACE_INCLUDE_DIRECTORIES ${SRTP_INCLUDE_DIRS}
+				IMPORTED_LINK_INTERFACE_LANGUAGES C
+				IMPORTED_LOCATION ${SRTP_LIBRARIES})
+		endif()
+		target_compile_definitions(datachannel PRIVATE RTC_SYSTEM_SRTP=1)
+		target_compile_definitions(datachannel-static PRIVATE RTC_SYSTEM_SRTP=1)
+		target_link_libraries(datachannel PRIVATE SRTP::SRTP)
+		target_link_libraries(datachannel-static PRIVATE SRTP::SRTP)
+	else()
+		add_subdirectory(deps/libsrtp EXCLUDE_FROM_ALL)
+		target_compile_definitions(datachannel PRIVATE RTC_SYSTEM_SRTP=0)
+		target_compile_definitions(datachannel-static PRIVATE RTC_SYSTEM_SRTP=0)
+		target_link_libraries(datachannel PRIVATE srtp2)
+		target_link_libraries(datachannel-static PRIVATE srtp2)
+	endif()
 endif()
 endif()
 
 
 if (USE_GNUTLS)
 if (USE_GNUTLS)
@@ -300,6 +300,7 @@ if(NOT NO_EXAMPLES AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
 	add_subdirectory(deps/json)
 	add_subdirectory(deps/json)
 	add_subdirectory(examples/client)
 	add_subdirectory(examples/client)
 	add_subdirectory(examples/media)
 	add_subdirectory(examples/media)
+	add_subdirectory(examples/sfu-media)
 	add_subdirectory(examples/copy-paste)
 	add_subdirectory(examples/copy-paste)
 	add_subdirectory(examples/copy-paste-capi)
 	add_subdirectory(examples/copy-paste-capi)
 endif()
 endif()

+ 27 - 3
README.md

@@ -19,7 +19,7 @@ The WebRTC stack has been tested to be compatible with Firefox and Chromium.
 
 
 Protocol stack:
 Protocol stack:
 - SCTP-based Data Channels ([draft-ietf-rtcweb-data-channel-13](https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13))
 - SCTP-based Data Channels ([draft-ietf-rtcweb-data-channel-13](https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13))
-- SRTP-based Media Transport ([draft-ietf-rtcweb-rtp-usage-26](https://tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage-26)) with [libSRTP](https://github.com/cisco/libsrtp)
+- SRTP-based Media Transport ([draft-ietf-rtcweb-rtp-usage-26](https://tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage-26))
 - DTLS/UDP ([RFC7350](https://tools.ietf.org/html/rfc7350) and [RFC8261](https://tools.ietf.org/html/rfc8261))
 - DTLS/UDP ([RFC7350](https://tools.ietf.org/html/rfc7350) and [RFC8261](https://tools.ietf.org/html/rfc8261))
 - ICE ([RFC8445](https://tools.ietf.org/html/rfc8445)) with STUN ([RFC5389](https://tools.ietf.org/html/rfc5389))
 - ICE ([RFC8445](https://tools.ietf.org/html/rfc8445)) with STUN ([RFC5389](https://tools.ietf.org/html/rfc5389))
 
 
@@ -53,10 +53,11 @@ Dependencies:
 Submodules:
 Submodules:
 - libjuice: https://github.com/paullouisageneau/libjuice
 - libjuice: https://github.com/paullouisageneau/libjuice
 - usrsctp: https://github.com/sctplab/usrsctp
 - usrsctp: https://github.com/sctplab/usrsctp
+- libsrtp: https://github.com/cisco/libsrtp
 
 
 Optional dependencies:
 Optional dependencies:
-- libnice: https://nice.freedesktop.org/ (only if selected as ICE backend instead of libjuice)
-- libSRTP: https://github.com/cisco/libsrtp (only necessary for supporting media transport)
+- libnice: https://nice.freedesktop.org/ (if selected as ICE backend instead of libjuice)
+- libsrtp: https://github.com/cisco/libsrtp (if selected instead of the submodule)
 
 
 ## Building
 ## Building
 
 
@@ -81,6 +82,29 @@ $ cd build
 $ make -j2
 $ make -j2
 ```
 ```
 
 
+#### Apple macOS with XCode project
+
+```bash
+$ cmake -B "$BUILD_DIR" -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode
+```
+
+Xcode project is generated in *build/* directory.
+
+##### Solving **Could NOT find OpenSSL** error
+
+You need to add OpenSSL root directory if your build fails with the following message:
+
+```
+Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the
+system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY
+OPENSSL_INCLUDE_DIR)
+```
+
+for example:
+```bash
+$ cmake -B build -DUSE_GNUTLS=0 -DUSE_NICE=0 -G Xcode -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl\@1.1/1.1.1h/
+```
+
 #### Microsoft Windows with MinGW cross-compilation
 #### Microsoft Windows with MinGW cross-compilation
 ```bash
 ```bash
 $ cmake -B build -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
 $ cmake -B build -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file

+ 1 - 1
deps/libjuice

@@ -1 +1 @@
-Subproject commit 0a44ac2d26959fcdda204fa6814b39624ebf84d1
+Subproject commit 7afe3940dd73fde781333af556310f8a499cff40

+ 1 - 0
deps/libsrtp

@@ -0,0 +1 @@
+Subproject commit d02d21111e379c297e93a9033d7b653135f732ee

+ 4 - 0
examples/client/getopt.cpp

@@ -36,7 +36,11 @@ PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
 YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
 YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
 EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 */
 */
+
+#ifndef _CRT_SECURE_NO_WARNINGS
 #define _CRT_SECURE_NO_WARNINGS
 #define _CRT_SECURE_NO_WARNINGS
+#endif
+
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <malloc.h>
 #include <malloc.h>

+ 2 - 2
examples/media/main.cpp

@@ -1,6 +1,6 @@
 /*
 /*
  * libdatachannel client example
  * libdatachannel client example
- * Copyright (c) 2020 Staz M
+ * Copyright (c) 2020 Staz Modrzynski
  * Copyright (c) 2020 Paul-Louis Ageneau
  * Copyright (c) 2020 Paul-Louis Ageneau
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
@@ -67,7 +67,7 @@ int main() {
 
 
 		auto track = pc->addTrack(media);
 		auto track = pc->addTrack(media);
 
 
-		auto session = std::make_shared<rtc::RtcpSession>();
+		auto session = std::make_shared<rtc::RtcpReceivingSession>();
 		track->setRtcpHandler(session);
 		track->setRtcpHandler(session);
 
 
 		track->onMessage(
 		track->onMessage(

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

@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.7)
+
+add_executable(datachannel-sfu-media main.cpp)
+set_target_properties(datachannel-sfu-media PROPERTIES
+		CXX_STANDARD 17
+		OUTPUT_NAME sfu-media)
+
+if(WIN32)
+	target_link_libraries(datachannel-sfu-media datachannel-static) # DLL exports only the C API
+else()
+	target_link_libraries(datachannel-sfu-media datachannel)
+endif()
+
+target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)
+

+ 134 - 0
examples/sfu-media/main.cpp

@@ -0,0 +1,134 @@
+/*
+ * libdatachannel client example
+ * Copyright (c) 2020 Staz Modrzynski
+ * Copyright (c) 2020 Paul-Louis Ageneau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+
+#include "rtc/rtc.hpp"
+
+#include <iostream>
+#include <memory>
+
+#include <nlohmann/json.hpp>
+
+using nlohmann::json;
+
+struct Receiver {
+	std::shared_ptr<rtc::PeerConnection> conn;
+	std::shared_ptr<rtc::Track> track;
+};
+int main() {
+	std::vector<std::shared_ptr<Receiver>> receivers;
+
+	try {
+		rtc::InitLogger(rtc::LogLevel::Info);
+
+		auto pc = std::make_shared<rtc::PeerConnection>();
+		pc->onStateChange(
+		    [](rtc::PeerConnection::State state) { std::cout << "State: " << state << std::endl; });
+		pc->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
+			std::cout << "Gathering State: " << state << std::endl;
+			if (state == rtc::PeerConnection::GatheringState::Complete) {
+				auto description = pc->localDescription();
+				json message = {{"type", description->typeString()},
+				                {"sdp", std::string(description.value())}};
+				std::cout << "Please copy/paste this offer to the SENDER: " << message << std::endl;
+			}
+		});
+
+		rtc::Description::Video media("video", rtc::Description::Direction::RecvOnly);
+		media.addH264Codec(96);
+		media.setBitrate(
+		    3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
+
+		auto track = pc->addTrack(media);
+		pc->setLocalDescription();
+
+		auto session = std::make_shared<rtc::RtcpReceivingSession>();
+		track->setRtcpHandler(session);
+
+		const rtc::SSRC targetSSRC = 4;
+
+		track->onMessage(
+		    [&receivers, targetSSRC](rtc::binary message) {
+			    // This is an RTP packet
+			    auto rtp = (rtc::RTP *)message.data();
+			    rtp->setSsrc(targetSSRC);
+			    for (auto pc : receivers) {
+				    if (pc->track != nullptr && pc->track->isOpen()) {
+					    pc->track->send(message);
+				    }
+			    }
+		    },
+		    nullptr);
+
+		// Set the SENDERS Answer
+		{
+			std::cout << "Please copy/paste the answer provided by the SENDER: " << std::endl;
+			std::string sdp;
+			std::getline(std::cin, sdp);
+			std::cout << "Got answer" << sdp << std::endl;
+			json j = json::parse(sdp);
+			rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
+			pc->setRemoteDescription(answer);
+		}
+
+		// For each receiver
+		while (true) {
+			auto pc = std::make_shared<Receiver>();
+			pc->conn = std::make_shared<rtc::PeerConnection>();
+			pc->conn->onStateChange([](rtc::PeerConnection::State state) {
+				std::cout << "State: " << state << std::endl;
+			});
+			pc->conn->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
+				std::cout << "Gathering State: " << state << std::endl;
+				if (state == rtc::PeerConnection::GatheringState::Complete) {
+					auto description = pc->conn->localDescription();
+					json message = {{"type", description->typeString()},
+					                {"sdp", std::string(description.value())}};
+					std::cout << "Please copy/paste this offer to the RECEIVER: " << message
+					          << std::endl;
+				}
+			});
+			rtc::Description::Video media("video", rtc::Description::Direction::SendOnly);
+			media.addH264Codec(96);
+			media.setBitrate(
+			    3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
+
+			media.addSSRC(targetSSRC, "video-send");
+
+			pc->track = pc->conn->addTrack(media);
+			pc->conn->setLocalDescription();
+
+			pc->track->onMessage([](rtc::binary var) {}, nullptr);
+
+			std::cout << "Please copy/paste the answer provided by the RECEIVER: " << std::endl;
+			std::string sdp;
+			std::getline(std::cin, sdp);
+			std::cout << "Got answer" << sdp << std::endl;
+			json j = json::parse(sdp);
+			rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
+			pc->conn->setRemoteDescription(answer);
+
+			receivers.push_back(pc);
+		}
+
+	} catch (const std::exception &e) {
+		std::cerr << "Error: " << e.what() << std::endl;
+	}
+}

+ 87 - 0
examples/sfu-media/main.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>libdatachannel media example</title>
+</head>
+<body>
+
+<div style="display:inline-block; width:40%;">
+    <h1>SENDER</h1>
+    <p id="send-help">Please enter the offer provided to you by the application: </p>
+    <textarea style="width:100%;" id=send-text rows="50"></textarea>
+    <button id=send-btn>Submit</button>
+</div>
+<div style="display:inline-block; width:40%;">
+    <h1>RECEIVER</h1>
+    <p id="recv-help">Please enter the offer provided to you by the application: </p>
+    <textarea id=recv-text style="width:100%;" rows="50"></textarea>
+    <button id=recv-btn>Submit</button>
+</div>
+<div id="videos">
+
+</div>
+<script>
+    document.querySelector('#send-btn').addEventListener('click',  async () => {
+        let offer = JSON.parse(document.querySelector('#send-text').value);
+        rtc = new RTCPeerConnection({
+            // Recommended for libdatachannel
+            bundlePolicy: "max-bundle",
+        });
+
+        rtc.onicegatheringstatechange = (state) => {
+            if (rtc.iceGatheringState === 'complete') {
+                // We only want to provide an answer once all of our candidates have been added to the SDP.
+                let answer = rtc.localDescription;
+                document.querySelector('#send-text').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
+                document.querySelector('#send-help').value = 'Please paste the answer in the application.';
+                alert('Please paste the answer in the application.');
+            }
+        }
+        await rtc.setRemoteDescription(offer);
+
+        let media = await navigator.mediaDevices.getUserMedia({
+            video: {
+                width: 1280,
+                height: 720
+            }
+        });
+        media.getTracks().forEach(track => rtc.addTrack(track, media));
+        let answer = await rtc.createAnswer();
+        await rtc.setLocalDescription(answer);
+    });
+
+    document.querySelector('#recv-btn').addEventListener('click',  async () => {
+        let offer = JSON.parse(document.querySelector('#recv-text').value);
+        rtc = new RTCPeerConnection({
+            // Recommended for libdatachannel
+            bundlePolicy: "max-bundle",
+        });
+
+        rtc.onicegatheringstatechange = (state) => {
+            if (rtc.iceGatheringState === 'complete') {
+                // We only want to provide an answer once all of our candidates have been added to the SDP.
+                let answer = rtc.localDescription;
+                document.querySelector('#recv-text').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
+                document.querySelector('#recv-help').value = 'Please paste the answer in the application.';
+                alert('Please paste the answer in the application.');
+            }
+        }
+        let trackCount = 0;
+        rtc.ontrack = (ev) => {
+            let thisID = trackCount++;
+
+            document.querySelector("#videos").innerHTML += "<video width=100% height=100% id='video-" + thisID + "'></video>";
+            let tracks = [];
+            rtc.getReceivers().forEach(recv => tracks.push(recv.track));
+            document.querySelector("#video-" + thisID).srcObject = new MediaStream(tracks);
+            document.querySelector("#video-" + thisID).play();
+        };
+        await rtc.setRemoteDescription(offer);
+        let answer = await rtc.createAnswer();
+        await rtc.setLocalDescription(answer);
+    });
+</script>
+
+</body>
+</html>

+ 1 - 1
examples/web/script.js

@@ -55,7 +55,7 @@ function openSignaling(url) {
     const ws = new WebSocket(url);
     const ws = new WebSocket(url);
     ws.onopen = () => resolve(ws);
     ws.onopen = () => resolve(ws);
     ws.onerror = () => reject(new Error('WebSocket error'));
     ws.onerror = () => reject(new Error('WebSocket error'));
-    ws.onclosed = () => console.error('WebSocket disconnected');
+    ws.onclose = () => console.error('WebSocket disconnected');
     ws.onmessage = (e) => {
     ws.onmessage = (e) => {
       if(typeof(e.data) != 'string') return;
       if(typeof(e.data) != 'string') return;
       const message = JSON.parse(e.data);
       const message = JSON.parse(e.data);

+ 6 - 3
include/rtc/candidate.hpp

@@ -31,7 +31,11 @@ public:
 	enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
 	enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
 	enum class TransportType { Unknown, Udp, TcpActive, TcpPassive, TcpSo, TcpUnknown };
 	enum class TransportType { Unknown, Udp, TcpActive, TcpPassive, TcpSo, TcpUnknown };
 
 
-	Candidate(string candidate = "", string mid = "");
+	Candidate();
+	Candidate(string candidate);
+	Candidate(string candidate, string mid);
+
+	void hintMid(string mid);
 
 
 	enum class ResolveMode { Simple, Lookup };
 	enum class ResolveMode { Simple, Lookup };
 	bool resolve(ResolveMode mode = ResolveMode::Simple);
 	bool resolve(ResolveMode mode = ResolveMode::Simple);
@@ -50,7 +54,7 @@ public:
 
 
 private:
 private:
 	string mCandidate;
 	string mCandidate;
-	string mMid;
+	std::optional<string> mMid;
 
 
 	// Extracted on resolution
 	// Extracted on resolution
 	Family mFamily;
 	Family mFamily;
@@ -68,4 +72,3 @@ std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
 std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
 std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
 
 
 #endif
 #endif
-

+ 2 - 2
include/rtc/channel.hpp

@@ -54,7 +54,8 @@ public:
 
 
 	// Extended API
 	// Extended API
 	virtual std::optional<message_variant> receive() = 0; // only if onMessage unset
 	virtual std::optional<message_variant> receive() = 0; // only if onMessage unset
-	virtual size_t availableAmount() const; // total size available to receive
+	virtual std::optional<message_variant> peek() = 0;    // only if onMessage unset
+	virtual size_t availableAmount() const;               // total size available to receive
 	void onAvailable(std::function<void()> callback);
 	void onAvailable(std::function<void()> callback);
 
 
 protected:
 protected:
@@ -81,4 +82,3 @@ private:
 } // namespace rtc
 } // namespace rtc
 
 
 #endif // RTC_CHANNEL_H
 #endif // RTC_CHANNEL_H
-

+ 27 - 11
include/rtc/datachannel.hpp

@@ -36,15 +36,14 @@ namespace rtc {
 class SctpTransport;
 class SctpTransport;
 class PeerConnection;
 class PeerConnection;
 
 
-class DataChannel final : public std::enable_shared_from_this<DataChannel>, public Channel {
+class DataChannel : public std::enable_shared_from_this<DataChannel>, public Channel {
 public:
 public:
-	DataChannel(std::weak_ptr<PeerConnection> pc, unsigned int stream, string label,
-	            string protocol, Reliability reliability);
-	DataChannel(std::weak_ptr<PeerConnection> pc, std::weak_ptr<SctpTransport> transport,
-	            unsigned int stream);
-	~DataChannel();
+	DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
+	            Reliability reliability);
+	virtual ~DataChannel();
 
 
-	unsigned int stream() const;
+	uint16_t stream() const;
+	uint16_t id() const;
 	string label() const;
 	string label() const;
 	string protocol() const;
 	string protocol() const;
 	Reliability reliability() const;
 	Reliability reliability() const;
@@ -62,18 +61,19 @@ public:
 	// Extended API
 	// Extended API
 	size_t availableAmount() const override;
 	size_t availableAmount() const override;
 	std::optional<message_variant> receive() override;
 	std::optional<message_variant> receive() override;
+	std::optional<message_variant> peek() override;
 
 
-private:
+protected:
+	virtual void open(std::shared_ptr<SctpTransport> transport);
+	virtual void processOpenMessage(message_ptr message);
 	void remoteClose();
 	void remoteClose();
-	void open(std::shared_ptr<SctpTransport> transport);
 	bool outgoing(message_ptr message);
 	bool outgoing(message_ptr message);
 	void incoming(message_ptr message);
 	void incoming(message_ptr message);
-	void processOpenMessage(message_ptr message);
 
 
 	const std::weak_ptr<PeerConnection> mPeerConnection;
 	const std::weak_ptr<PeerConnection> mPeerConnection;
 	std::weak_ptr<SctpTransport> mSctpTransport;
 	std::weak_ptr<SctpTransport> mSctpTransport;
 
 
-	unsigned int mStream;
+	uint16_t mStream;
 	string mLabel;
 	string mLabel;
 	string mProtocol;
 	string mProtocol;
 	std::shared_ptr<Reliability> mReliability;
 	std::shared_ptr<Reliability> mReliability;
@@ -81,11 +81,27 @@ private:
 	std::atomic<bool> mIsOpen = false;
 	std::atomic<bool> mIsOpen = false;
 	std::atomic<bool> mIsClosed = false;
 	std::atomic<bool> mIsClosed = false;
 
 
+private:
 	Queue<message_ptr> mRecvQueue;
 	Queue<message_ptr> mRecvQueue;
 
 
 	friend class PeerConnection;
 	friend class PeerConnection;
 };
 };
 
 
+class NegociatedDataChannel final : public DataChannel {
+public:
+	NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
+	                      string protocol, Reliability reliability);
+	NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, std::weak_ptr<SctpTransport> transport,
+	                      uint16_t stream);
+	~NegociatedDataChannel();
+
+private:
+	void open(std::shared_ptr<SctpTransport> transport) override;
+	void processOpenMessage(message_ptr message) override;
+
+	friend class PeerConnection;
+};
+
 template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer &buf) {
 template <typename Buffer> std::pair<const byte *, size_t> to_bytes(const Buffer &buf) {
 	using T = typename std::remove_pointer<decltype(buf.data())>::type;
 	using T = typename std::remove_pointer<decltype(buf.data())>::type;
 	using E = typename std::conditional<std::is_void<T>::value, byte, T>::type;
 	using E = typename std::conditional<std::is_void<T>::value, byte, T>::type;

+ 41 - 16
include/rtc/description.hpp

@@ -1,6 +1,6 @@
 /**
 /**
  * Copyright (c) 2019-2020 Paul-Louis Ageneau
  * Copyright (c) 2019-2020 Paul-Louis Ageneau
- * Copyright (c) 2020 Staz M
+ * Copyright (c) 2020 Staz Modrzynski
  *
  *
  * This library is free software; you can redistribute it and/or
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * modify it under the terms of the GNU Lesser General Public
@@ -38,9 +38,8 @@ public:
 	enum class Role { ActPass, Passive, Active };
 	enum class Role { ActPass, Passive, Active };
 	enum class Direction { SendOnly, RecvOnly, SendRecv, Inactive, Unknown };
 	enum class Direction { SendOnly, RecvOnly, SendRecv, Inactive, Unknown };
 
 
-	Description(const string &sdp, const string &typeString = "");
-	Description(const string &sdp, Type type);
-	Description(const string &sdp, Type type, Role role);
+	Description(const string &sdp, Type type = Type::Unspec, Role role = Role::ActPass);
+	Description(const string &sdp, string typeString);
 
 
 	Type type() const;
 	Type type() const;
 	string typeString() const;
 	string typeString() const;
@@ -78,6 +77,10 @@ public:
 
 
 		virtual void parseSdpLine(string_view line);
 		virtual void parseSdpLine(string_view line);
 
 
+		std::vector<string>::iterator beginAttributes();
+		std::vector<string>::iterator endAttributes();
+		std::vector<string>::iterator removeAttribute(std::vector<string>::iterator iterator);
+
 	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;
@@ -127,10 +130,11 @@ public:
 
 
 		void removeFormat(const string &fmt);
 		void removeFormat(const string &fmt);
 
 
-		void addVideoCodec(int payloadType, const string &codec);
-		void addH264Codec(int payloadType);
-		void addVP8Codec(int payloadType);
-		void addVP9Codec(int payloadType);
+		void addSSRC(uint32_t ssrc, std::string name);
+		void addSSRC(uint32_t ssrc);
+		void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, string name);
+		bool hasSSRC(uint32_t ssrc);
+		std::vector<uint32_t> getSSRCs();
 
 
 		void setBitrate(int bitrate);
 		void setBitrate(int bitrate);
 		int getBitrate() const;
 		int getBitrate() const;
@@ -139,16 +143,13 @@ public:
 
 
 		virtual void parseSdpLine(string_view line) override;
 		virtual void parseSdpLine(string_view line) override;
 
 
-	private:
-		virtual string generateSdpLines(string_view eol) const override;
-
-		int mBas = -1;
-
 		struct RTPMap {
 		struct RTPMap {
 			RTPMap(string_view mline);
 			RTPMap(string_view mline);
+			RTPMap() {}
 
 
 			void removeFB(const string &string);
 			void removeFB(const string &string);
 			void addFB(const string &string);
 			void addFB(const string &string);
+			void addAttribute(std::string attr) { fmtps.emplace_back(attr); }
 
 
 			int pt;
 			int pt;
 			string format;
 			string format;
@@ -157,22 +158,46 @@ public:
 
 
 			std::vector<string> rtcpFbs;
 			std::vector<string> rtcpFbs;
 			std::vector<string> fmtps;
 			std::vector<string> fmtps;
+
+			static int parsePT(string_view view);
+			void setMLine(string_view view);
 		};
 		};
 
 
+		std::map<int, RTPMap>::iterator beginMaps();
+		std::map<int, RTPMap>::iterator endMaps();
+		std::map<int, RTPMap>::iterator removeMap(std::map<int, RTPMap>::iterator iterator);
+
+	private:
+		virtual string generateSdpLines(string_view eol) const override;
+
+		int mBas = -1;
+
 		Media::RTPMap &getFormat(int fmt);
 		Media::RTPMap &getFormat(int fmt);
 		Media::RTPMap &getFormat(const string &fmt);
 		Media::RTPMap &getFormat(const string &fmt);
 
 
 		std::map<int, RTPMap> mRtpMap;
 		std::map<int, RTPMap> mRtpMap;
+		std::vector<uint32_t> mSsrcs;
+
+	public:
+		void addRTPMap(const RTPMap &map);
 	};
 	};
 
 
 	class Audio : public Media {
 	class Audio : public Media {
 	public:
 	public:
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
 		Audio(string mid = "audio", Direction dir = Direction::SendOnly);
+
+		void addAudioCodec(int payloadType, const string &codec);
+		void addOpusCodec(int payloadType);
 	};
 	};
 
 
 	class Video : public Media {
 	class Video : public Media {
 	public:
 	public:
 		Video(string mid = "video", Direction dir = Direction::SendOnly);
 		Video(string mid = "video", Direction dir = Direction::SendOnly);
+
+		void addVideoCodec(int payloadType, const string &codec);
+		void addH264Codec(int payloadType);
+		void addVP8Codec(int payloadType);
+		void addVP9Codec(int payloadType);
 	};
 	};
 
 
 	bool hasApplication() const;
 	bool hasApplication() const;
@@ -185,9 +210,9 @@ public:
 	int addVideo(string mid = "video", Direction dir = Direction::SendOnly);
 	int addVideo(string mid = "video", Direction dir = Direction::SendOnly);
 	int addAudio(string mid = "audio", Direction dir = Direction::SendOnly);
 	int addAudio(string mid = "audio", Direction dir = Direction::SendOnly);
 
 
-	std::variant<Media *, Application *> media(int index);
-	std::variant<const Media *, const Application *> media(int index) const;
-	int mediaCount() const;
+	std::variant<Media *, Application *> media(unsigned int index);
+	std::variant<const Media *, const Application *> media(unsigned int index) const;
+	unsigned int mediaCount() const;
 
 
 	Application *application();
 	Application *application();
 
 

+ 1 - 1
include/rtc/log.hpp

@@ -49,6 +49,6 @@ enum class LogLevel { // Don't change, it must match plog severity
 
 
 void InitLogger(LogLevel level);
 void InitLogger(LogLevel level);
 void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
 void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
-}
+} // namespace rtc
 
 
 #endif
 #endif

+ 17 - 9
include/rtc/peerconnection.hpp

@@ -50,6 +50,13 @@ class SctpTransport;
 using certificate_ptr = std::shared_ptr<Certificate>;
 using certificate_ptr = std::shared_ptr<Certificate>;
 using future_certificate_ptr = std::shared_future<certificate_ptr>;
 using future_certificate_ptr = std::shared_future<certificate_ptr>;
 
 
+struct DataChannelInit {
+	Reliability reliability = {};
+	bool negotiated = false;
+	std::optional<uint16_t> id = nullopt;
+	string protocol = "";
+};
+
 class PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
 class PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
 public:
 public:
 	enum class State : int {
 	enum class State : int {
@@ -75,7 +82,7 @@ public:
 		HaveRemotePranswer = RTC_SIGNALING_HAVE_REMOTE_PRANSWER,
 		HaveRemotePranswer = RTC_SIGNALING_HAVE_REMOTE_PRANSWER,
 	} rtcSignalingState;
 	} rtcSignalingState;
 
 
-	PeerConnection(void);
+	PeerConnection();
 	PeerConnection(const Configuration &config);
 	PeerConnection(const Configuration &config);
 	~PeerConnection();
 	~PeerConnection();
 
 
@@ -95,15 +102,14 @@ public:
 	bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
 	bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
 
 
 	void setLocalDescription(Description::Type type = Description::Type::Unspec);
 	void setLocalDescription(Description::Type type = Description::Type::Unspec);
+
 	void setRemoteDescription(Description description);
 	void setRemoteDescription(Description description);
 	void addRemoteCandidate(Candidate candidate);
 	void addRemoteCandidate(Candidate candidate);
 
 
-	std::shared_ptr<DataChannel> addDataChannel(string label, string protocol = "",
-	                                            Reliability reliability = {});
+	std::shared_ptr<DataChannel> addDataChannel(string label, DataChannelInit init = {});
 
 
 	// Equivalent to calling addDataChannel() and setLocalDescription()
 	// Equivalent to calling addDataChannel() and setLocalDescription()
-	std::shared_ptr<DataChannel> createDataChannel(string label, string protocol = "",
-	                                               Reliability reliability = {});
+	std::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);
 	void onLocalDescription(std::function<void(Description description)> callback);
 	void onLocalDescription(std::function<void(Description description)> callback);
@@ -133,9 +139,10 @@ private:
 	void forwardMessage(message_ptr message);
 	void forwardMessage(message_ptr message);
 	void forwardMedia(message_ptr message);
 	void forwardMedia(message_ptr message);
 	void forwardBufferedAmount(uint16_t stream, size_t amount);
 	void forwardBufferedAmount(uint16_t stream, size_t amount);
+	std::optional<std::string> getMidFromSsrc(uint32_t ssrc);
 
 
 	std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
 	std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
-	                                                string protocol, Reliability reliability);
+	                                                DataChannelInit init);
 	std::shared_ptr<DataChannel> findDataChannel(uint16_t stream);
 	std::shared_ptr<DataChannel> findDataChannel(uint16_t stream);
 	void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
 	void iterateDataChannels(std::function<void(std::shared_ptr<DataChannel> channel)> func);
 	void openDataChannels();
 	void openDataChannels();
@@ -173,11 +180,12 @@ private:
 	std::shared_ptr<DtlsTransport> mDtlsTransport;
 	std::shared_ptr<DtlsTransport> mDtlsTransport;
 	std::shared_ptr<SctpTransport> mSctpTransport;
 	std::shared_ptr<SctpTransport> mSctpTransport;
 
 
-	std::unordered_map<unsigned int, std::weak_ptr<DataChannel>> mDataChannels; // by stream ID
-	std::unordered_map<string, std::weak_ptr<Track>> mTracks;                   // by mid
+	std::unordered_map<uint16_t, std::weak_ptr<DataChannel>> mDataChannels; // by stream ID
+	std::unordered_map<string, std::weak_ptr<Track>> mTracks;               // by mid
+	std::vector<std::weak_ptr<Track>> mTrackLines;                          // by SDP order
 	std::shared_mutex mDataChannelsMutex, mTracksMutex;
 	std::shared_mutex mDataChannelsMutex, mTracksMutex;
 
 
-	std::unordered_map<unsigned int, string> mMidFromPayloadType; // cache
+	std::unordered_map<uint32_t, string> mMidFromSsrc; // cache
 
 
 	std::atomic<State> mState;
 	std::atomic<State> mState;
 	std::atomic<GatheringState> mGatheringState;
 	std::atomic<GatheringState> mGatheringState;

+ 0 - 1
include/rtc/queue.hpp

@@ -170,4 +170,3 @@ template <typename T> std::optional<T> Queue<T>::popImpl() {
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif
-

+ 0 - 1
include/rtc/reliability.hpp

@@ -37,4 +37,3 @@ struct Reliability {
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif
-

+ 35 - 21
include/rtc/rtc.h

@@ -78,8 +78,10 @@ typedef enum { // Don't change, it must match plog severity
 } rtcLogLevel;
 } rtcLogLevel;
 
 
 #define RTC_ERR_SUCCESS 0
 #define RTC_ERR_SUCCESS 0
-#define RTC_ERR_INVALID -1 // invalid argument
-#define RTC_ERR_FAILURE -2 // runtime error
+#define RTC_ERR_INVALID -1   // invalid argument
+#define RTC_ERR_FAILURE -2   // runtime error
+#define RTC_ERR_NOT_AVAIL -3 // element not available
+#define RTC_ERR_TOO_SMALL -4 // buffer too small
 
 
 typedef struct {
 typedef struct {
 	const char **iceServers;
 	const char **iceServers;
@@ -95,20 +97,30 @@ typedef struct {
 	unsigned int maxRetransmits;    // ignored if reliable
 	unsigned int maxRetransmits;    // ignored if reliable
 } rtcReliability;
 } rtcReliability;
 
 
-typedef void (RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
-typedef void (RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const char *type, void *ptr);
-typedef void (RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid, void *ptr);
-typedef void (RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState 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 *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr);
-typedef void (RTC_API *rtcTrackCallbackFunc)(int pc, int tr, void *ptr);
-typedef void (RTC_API *rtcOpenCallbackFunc)(int id, void *ptr);
-typedef void (RTC_API *rtcClosedCallbackFunc)(int id, void *ptr);
-typedef void (RTC_API *rtcErrorCallbackFunc)(int id, const char *error, void *ptr);
-typedef void (RTC_API *rtcMessageCallbackFunc)(int id, const char *message, int size, void *ptr);
-typedef void (RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr);
-typedef void (RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr);
+typedef struct {
+	rtcReliability reliability;
+	const char *protocol; // empty string if NULL
+	bool negotiated;
+	bool manualStream;
+	uint16_t stream; // numeric ID 0-65534, ignored if manualStream is false
+} rtcDataChannelInit;
+
+typedef void(RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
+typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const char *type,
+                                                  void *ptr);
+typedef void(RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid,
+                                                void *ptr);
+typedef void(RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState 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 *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr);
+typedef void(RTC_API *rtcTrackCallbackFunc)(int pc, int tr, void *ptr);
+typedef void(RTC_API *rtcOpenCallbackFunc)(int id, void *ptr);
+typedef void(RTC_API *rtcClosedCallbackFunc)(int id, void *ptr);
+typedef void(RTC_API *rtcErrorCallbackFunc)(int id, const char *error, void *ptr);
+typedef void(RTC_API *rtcMessageCallbackFunc)(int id, const char *message, int size, void *ptr);
+typedef void(RTC_API *rtcBufferedAmountLowCallbackFunc)(int id, void *ptr);
+typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr);
 
 
 // Log
 // Log
 // NULL cb on the first call will log to stdout
 // NULL cb on the first call will log to stdout
@@ -137,19 +149,21 @@ RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 
 
-RTC_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote, int remoteSize);
+RTC_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote,
+                                           int remoteSize);
 
 
 // DataChannel
 // DataChannel
 RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
 RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
 RTC_EXPORT int rtcAddDataChannel(int pc, const char *label); // returns dc id
 RTC_EXPORT int rtcAddDataChannel(int pc, const char *label); // returns dc id
-RTC_EXPORT int rtcAddDataChannelExt(int pc, const char *label, const char *protocol,
-                                    const rtcReliability *reliability); // returns dc id
+RTC_EXPORT int rtcAddDataChannelEx(int pc, const char *label,
+                                   const rtcDataChannelInit *init); // returns dc id
 // Equivalent to calling rtcAddDataChannel() and rtcSetLocalDescription()
 // Equivalent to calling rtcAddDataChannel() and rtcSetLocalDescription()
 RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
 RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
-RTC_EXPORT int rtcCreateDataChannelExt(int pc, const char *label, const char *protocol,
-                                       const rtcReliability *reliability); // returns dc id
+RTC_EXPORT int rtcCreateDataChannelEx(int pc, const char *label,
+                                      const rtcDataChannelInit *init); // returns dc id
 RTC_EXPORT int rtcDeleteDataChannel(int dc);
 RTC_EXPORT int rtcDeleteDataChannel(int dc);
 
 
+RTC_EXPORT int rtcGetDataChannelStream(int dc);
 RTC_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability);
 RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability);

+ 0 - 1
include/rtc/rtc.hpp

@@ -27,4 +27,3 @@
 
 
 // C API
 // C API
 #include "rtc.h"
 #include "rtc.h"
-

+ 43 - 11
include/rtc/rtcp.hpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2020 Staz M
+ * Copyright (c) 2020 Staz Modrzynski
  * Copyright (c) 2020 Paul-Louis Ageneau
  * Copyright (c) 2020 Paul-Louis Ageneau
  *
  *
  * This library is free software; you can redistribute it and/or
  * This library is free software; you can redistribute it and/or
@@ -20,35 +20,67 @@
 #ifndef RTC_RTCP_H
 #ifndef RTC_RTCP_H
 #define RTC_RTCP_H
 #define RTC_RTCP_H
 
 
+#include <utility>
+
 #include "include.hpp"
 #include "include.hpp"
 #include "log.hpp"
 #include "log.hpp"
 #include "message.hpp"
 #include "message.hpp"
+#include "rtp.hpp"
 
 
 namespace rtc {
 namespace rtc {
 
 
-typedef uint32_t SSRC;
-
 class RtcpHandler {
 class RtcpHandler {
+protected:
+	/**
+	 * Use this callback when trying to send custom data (such as RTCP) to the client.
+	 */
+	synchronized_callback<rtc::message_ptr> outgoingCallback;
+
 public:
 public:
-	virtual void onOutgoing(std::function<void(rtc::message_ptr)> cb) = 0;
-	virtual std::optional<rtc::message_ptr> incoming(rtc::message_ptr ptr) = 0;
+	/**
+	 * Called when there is traffic coming from the peer
+	 * @param ptr
+	 * @return
+	 */
+	virtual rtc::message_ptr incoming(rtc::message_ptr ptr) = 0;
+
+	/**
+	 * Called when there is traffic that needs to be sent to the peer
+	 * @param ptr
+	 * @return
+	 */
+	virtual rtc::message_ptr outgoing(rtc::message_ptr ptr) = 0;
+
+	/**
+	 * This callback is used to send traffic back to the peer.
+	 * This callback skips calling the track's methods.
+	 * @param cb
+	 */
+	void onOutgoing(const std::function<void(rtc::message_ptr)> &cb);
+
+	virtual bool requestKeyframe() { return false; }
 };
 };
 
 
+class Track;
+
 // 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 RtcpSession : public RtcpHandler {
+class RtcpReceivingSession : public RtcpHandler {
 public:
 public:
-	void onOutgoing(std::function<void(rtc::message_ptr)> cb) override;
+	rtc::message_ptr incoming(rtc::message_ptr ptr) override;
+	rtc::message_ptr outgoing(rtc::message_ptr ptr) override;
+	bool send(rtc::message_ptr ptr);
 
 
-	std::optional<rtc::message_ptr> incoming(rtc::message_ptr ptr) override;
 	void requestBitrate(unsigned int newBitrate);
 	void requestBitrate(unsigned int newBitrate);
 
 
-private:
+	bool requestKeyframe() override;
+
+protected:
 	void pushREMB(unsigned int bitrate);
 	void pushREMB(unsigned int bitrate);
 	void pushRR(unsigned int lastSR_delay);
 	void pushRR(unsigned int lastSR_delay);
-	void tx(message_ptr msg);
+
+	void pushPLI();
 
 
 	unsigned int mRequestedBitrate = 0;
 	unsigned int mRequestedBitrate = 0;
-	synchronized_callback<rtc::message_ptr> mTxCallback;
 	SSRC mSsrc = 0;
 	SSRC mSsrc = 0;
 	uint32_t mGreatestSeqNo = 0;
 	uint32_t mGreatestSeqNo = 0;
 	uint64_t mSyncRTPTS, mSyncNTPTS;
 	uint64_t mSyncRTPTS, mSyncNTPTS;

+ 493 - 0
include/rtc/rtp.hpp

@@ -0,0 +1,493 @@
+/**
+ * Copyright (c) 2020 Staz Modrzynski
+ * Copyright (c) 2020 Paul-Louis Ageneau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RTC_RTP_HPP
+#define RTC_RTP_HPP
+
+#include <rtc/log.hpp>
+
+#include <cmath>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#ifndef htonll
+#define htonll(x)                                                                                  \
+	((uint64_t)htonl(((uint64_t)(x)&0xFFFFFFFF) << 32) | (uint64_t)htonl((uint64_t)(x) >> 32))
+#endif
+#ifndef ntohll
+#define ntohll(x) htonll(x)
+#endif
+
+namespace rtc {
+
+typedef uint32_t SSRC;
+
+#pragma pack(push, 1)
+
+struct RTP {
+private:
+	uint8_t _first;
+	uint8_t _payloadType;
+	uint16_t _seqNumber;
+	uint32_t _timestamp;
+	SSRC _ssrc;
+
+public:
+	SSRC csrc[16];
+
+	inline uint8_t version() const { return _first >> 6; }
+	inline bool padding() const { return (_first >> 5) & 0x01; }
+	inline uint8_t csrcCount() const { return _first & 0x0F; }
+	inline uint8_t marker() const { return _payloadType & 0b10000000; }
+	inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
+	inline uint16_t seqNumber() const { return ntohs(_seqNumber); }
+	inline uint32_t timestamp() const { return ntohl(_timestamp); }
+	inline uint32_t ssrc() const { return ntohl(_ssrc); }
+
+	inline size_t getSize() const {
+		return ((char *)&csrc) - ((char *)this) + sizeof(SSRC) * csrcCount();
+	}
+
+	char *getBody() const { return ((char *)&csrc) + sizeof(SSRC) * csrcCount(); }
+
+	inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
+	inline void setPayloadType(uint8_t newPayloadType) {
+		_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
+	}
+	inline void setSsrc(uint32_t ssrc) { _ssrc = htonl(ssrc); }
+
+	void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
+};
+
+struct RTCP_ReportBlock {
+	SSRC ssrc;
+
+private:
+	uint32_t _fractionLostAndPacketsLost; // fraction lost is 8-bit, packets lost is 24-bit
+	uint16_t _seqNoCycles;
+	uint16_t _highestSeqNo;
+	uint32_t _jitter;
+	uint32_t _lastReport;
+	uint32_t _delaySinceLastReport;
+
+public:
+	inline void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost,
+	                          [[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
+	                          uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
+	                          uint64_t lastSR_DELAY) {
+		setSeqNo(highestSeqNo, seqNoCycles);
+		setJitter(jitter);
+		setSSRC(ssrc);
+
+		// Middle 32 bits of NTP Timestamp
+		//		  this->lastReport = lastSR_NTP >> 16u;
+		setNTPOfSR(uint64_t(lastSR_NTP));
+		setDelaySinceSR(uint32_t(lastSR_DELAY));
+
+		// The delay, expressed in units of 1/65536 seconds
+		//		  this->delaySinceLastReport = lastSR_DELAY;
+	}
+
+	inline void setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); }
+	inline SSRC getSSRC() const { return ntohl(ssrc); }
+
+	inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
+	                           [[maybe_unused]] unsigned int totalPackets) {
+		// TODO Implement loss percentages.
+		_fractionLostAndPacketsLost = 0;
+	}
+	inline unsigned int getLossPercentage() const {
+		// TODO Implement loss percentages.
+		return 0;
+	}
+	inline unsigned int getPacketLostCount() const {
+		// TODO Implement total packets lost.
+		return 0;
+	}
+
+	inline uint16_t seqNoCycles() const { return ntohs(_seqNoCycles); }
+	inline uint16_t highestSeqNo() const { return ntohs(_highestSeqNo); }
+	inline uint32_t jitter() const { return ntohl(_jitter); }
+
+	inline void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
+		_highestSeqNo = htons(highestSeqNo);
+		_seqNoCycles = htons(seqNoCycles);
+	}
+
+	inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
+
+	inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
+	inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
+
+	inline void setDelaySinceSR(uint32_t sr) {
+		// The delay, expressed in units of 1/65536 seconds
+		_delaySinceLastReport = htonl(sr);
+	}
+	inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
+
+	inline void log() const {
+		PLOG_VERBOSE << "RTCP report block: "
+		             << "ssrc="
+		             << ntohl(ssrc)
+		             // TODO: Implement these reports
+		             //	<< ", fractionLost=" << fractionLost
+		             //	<< ", packetsLost=" << packetsLost
+		             << ", highestSeqNo=" << highestSeqNo() << ", seqNoCycles=" << seqNoCycles()
+		             << ", jitter=" << jitter() << ", lastSR=" << getNTPOfSR()
+		             << ", lastSRDelay=" << getDelaySinceSR();
+	}
+};
+
+struct RTCP_HEADER {
+private:
+	uint8_t _first;
+	uint8_t _payloadType;
+	uint16_t _length;
+
+public:
+	inline uint8_t version() const { return _first >> 6; }
+	inline bool padding() const { return (_first >> 5) & 0x01; }
+	inline uint8_t reportCount() const { return _first & 0x0F; }
+	inline uint8_t payloadType() const { return _payloadType; }
+	inline uint16_t length() const { return ntohs(_length); }
+	inline size_t lengthInBytes() const { return (1 + length()) * 4; }
+
+	inline void setPayloadType(uint8_t type) { _payloadType = type; }
+	inline void setReportCount(uint8_t count) {
+		_first = (_first & 0b11100000u) | (count & 0b00011111u);
+	}
+	inline void setLength(uint16_t length) { _length = htons(length); }
+
+	inline void prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length) {
+		_first = 0b10000000; // version 2, no padding
+		setReportCount(reportCount);
+		setPayloadType(payloadType);
+		setLength(length);
+	}
+
+	inline void log() const {
+		PLOG_VERBOSE << "RTCP header: "
+		          << "version=" << unsigned(version()) << ", padding=" << padding()
+		          << ", reportCount=" << unsigned(reportCount())
+		          << ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
+	}
+};
+
+struct RTCP_FB_HEADER {
+	RTCP_HEADER header;
+	SSRC packetSender;
+	SSRC mediaSource;
+
+	[[nodiscard]] SSRC getPacketSenderSSRC() const { return ntohl(packetSender); }
+
+	[[nodiscard]] SSRC getMediaSourceSSRC() const { return ntohl(mediaSource); }
+
+	void setPacketSenderSSRC(SSRC ssrc) { this->packetSender = htonl(ssrc); }
+
+	void setMediaSourceSSRC(SSRC ssrc) { this->mediaSource = htonl(ssrc); }
+
+	void log() {
+		header.log();
+		PLOG_VERBOSE << "FB: "
+		             << " packet sender: " << getPacketSenderSSRC()
+		             << " media source: " << getMediaSourceSSRC();
+	}
+};
+
+struct RTCP_SR {
+	RTCP_HEADER header;
+	SSRC _senderSSRC;
+
+private:
+	uint64_t _ntpTimestamp;
+	uint32_t _rtpTimestamp;
+	uint32_t _packetCount;
+	uint32_t _octetCount;
+
+	RTCP_ReportBlock _reportBlocks;
+
+public:
+	inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
+		unsigned int length =
+		    ((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
+		header.prepareHeader(200, reportCount, uint16_t(length));
+		this->_senderSSRC = htonl(senderSSRC);
+	}
+
+	inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
+	inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
+
+	[[nodiscard]] inline size_t getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + size_t(header.length()));
+	}
+
+	inline uint64_t ntpTimestamp() const { return ntohll(_ntpTimestamp); }
+	inline uint32_t rtpTimestamp() const { return ntohl(_rtpTimestamp); }
+	inline uint32_t packetCount() const { return ntohl(_packetCount); }
+	inline uint32_t octetCount() const { return ntohl(_octetCount); }
+	inline uint32_t senderSSRC() const { return ntohl(_senderSSRC); }
+
+	inline void setNtpTimestamp(uint32_t ts) { _ntpTimestamp = htonll(ts); }
+	inline void setRtpTimestamp(uint32_t ts) { _rtpTimestamp = htonl(ts); }
+
+	inline void log() const {
+		header.log();
+		PLOG_VERBOSE << "RTCP SR: "
+		             << " SSRC=" << senderSSRC() << ", NTP_TS=" << ntpTimestamp()
+		             << ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
+		             << ", octetCount=" << octetCount();
+
+		for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
+			getReportBlock(i)->log();
+		}
+	}
+};
+
+struct RTCP_RR {
+	RTCP_HEADER header;
+	SSRC _senderSSRC;
+
+private:
+	RTCP_ReportBlock _reportBlocks;
+
+public:
+	inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
+	inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
+
+	inline SSRC senderSSRC() const { return ntohl(_senderSSRC); }
+	inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); }
+
+	[[nodiscard]] inline size_t getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + size_t(header.length()));
+	}
+
+	inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1;
+		header.prepareHeader(201, reportCount, uint16_t(length));
+		this->_senderSSRC = htonl(senderSSRC);
+	}
+
+	inline static size_t sizeWithReportBlocks(uint8_t reportCount) {
+		return sizeof(header) + 4 + size_t(reportCount) * sizeof(RTCP_ReportBlock);
+	}
+
+	inline bool isSenderReport() { return header.payloadType() == 200; }
+
+	inline bool isReceiverReport() { return header.payloadType() == 201; }
+
+	inline void log() const {
+		header.log();
+		PLOG_VERBOSE << "RTCP RR: "
+		             << " SSRC=" << ntohl(_senderSSRC);
+
+		for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
+			getReportBlock(i)->log();
+		}
+	}
+};
+
+struct RTCP_REMB {
+	RTCP_FB_HEADER header;
+
+	/*! \brief Unique identifier ('R' 'E' 'M' 'B') */
+	char id[4];
+
+	/*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
+	uint32_t bitrate;
+
+	SSRC ssrc[1];
+
+	[[nodiscard]] unsigned int getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + header.header.length());
+	}
+
+	void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
+
+		// Report Count becomes the format here.
+		header.header.prepareHeader(206, 15, 0);
+
+		// Always zero.
+		header.setMediaSourceSSRC(0);
+
+		header.setPacketSenderSSRC(senderSSRC);
+
+		id[0] = 'R';
+		id[1] = 'E';
+		id[2] = 'M';
+		id[3] = 'B';
+
+		setBitrate(numSSRC, bitrate);
+	}
+
+	void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
+		unsigned int exp = 0;
+		while (bitrate > pow(2, 18) - 1) {
+			exp++;
+			bitrate /= 2;
+		}
+
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		header.header.setLength(
+		    uint16_t((offsetof(RTCP_REMB, ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
+
+		this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate);
+	}
+
+	void setSsrc(int iterator, SSRC newSssrc) { ssrc[iterator] = htonl(newSssrc); }
+
+	size_t static inline sizeWithSSRCs(int count) {
+		return sizeof(RTCP_REMB) + (count - 1) * sizeof(SSRC);
+	}
+};
+
+struct RTCP_PLI {
+	RTCP_FB_HEADER header;
+
+	void preparePacket(SSRC messageSSRC) {
+		header.header.prepareHeader(206, 1, 2);
+		header.setPacketSenderSSRC(messageSSRC);
+		header.setMediaSourceSSRC(messageSSRC);
+	}
+
+	void print() { header.log(); }
+
+	[[nodiscard]] static unsigned int size() { return sizeof(RTCP_FB_HEADER); }
+};
+
+struct RTCP_FIR_PART {
+	uint32_t ssrc;
+	uint8_t seqNo;
+	uint8_t dummy1;
+	uint16_t dummy2;
+};
+
+struct RTCP_FIR {
+	RTCP_FB_HEADER header;
+	RTCP_FIR_PART parts[1];
+
+	void preparePacket(SSRC messageSSRC, uint8_t seqNo) {
+		header.header.prepareHeader(206, 4, 2 + 2 * 1);
+		header.setPacketSenderSSRC(messageSSRC);
+		header.setMediaSourceSSRC(messageSSRC);
+		parts[0].ssrc = htonl(messageSSRC);
+		parts[0].seqNo = seqNo;
+	}
+
+	void print() { header.log(); }
+
+	[[nodiscard]] static unsigned int size() {
+		return sizeof(RTCP_FB_HEADER) + sizeof(RTCP_FIR_PART);
+	}
+};
+
+struct RTCP_NACK_PART {
+	uint16_t pid;
+	uint16_t blp;
+};
+
+class RTCP_NACK {
+public:
+	RTCP_FB_HEADER header;
+	RTCP_NACK_PART parts[1];
+
+public:
+	void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
+		header.header.prepareHeader(205, 1, 2 + discreteSeqNoCount);
+		header.setMediaSourceSSRC(ssrc);
+		header.setPacketSenderSSRC(ssrc);
+	}
+
+	/**
+	 * Add a packet to the list of missing packets.
+	 * @param fciCount The number of FCI fields that are present in this packet.
+	 *                  Let the number start at zero and let this function grow the number.
+	 * @param fciPID The seq no of the active FCI. It will be initialized automatically, and will
+	 * change automatically.
+	 * @param missingPacket The seq no of the missing packet. This will be added to the queue.
+	 * @return true if the packet has grown, false otherwise.
+	 */
+	bool addMissingPacket(unsigned int *fciCount, uint16_t *fciPID, const uint16_t &missingPacket) {
+		if (*fciCount == 0 || missingPacket < *fciPID || missingPacket > (*fciPID + 16)) {
+			parts[*fciCount].pid = htons(missingPacket);
+			parts[*fciCount].blp = 0;
+			*fciPID = missingPacket;
+			(*fciCount)++;
+			return true;
+		} else {
+			// TODO SPEEED!
+			parts[(*fciCount) - 1].blp = htons(ntohs(parts[(*fciCount) - 1].blp) |
+			                                   (1u << (unsigned int)(missingPacket - *fciPID)));
+			return false;
+		}
+	}
+
+	[[nodiscard]] static unsigned int getSize(unsigned int discreteSeqNoCount) {
+		return offsetof(RTCP_NACK, parts) + sizeof(RTCP_NACK_PART) * discreteSeqNoCount;
+	}
+
+	[[nodiscard]] unsigned int getSeqNoCount() { return header.header.length() - 2; }
+};
+
+class RTP_RTX {
+private:
+	RTP header;
+
+public:
+	size_t copyTo(RTP *dest, size_t totalSize, uint8_t originalPayloadType) {
+		memmove((char *)dest, (char *)this, header.getSize());
+		dest->setSeqNumber(getOriginalSeqNo());
+		dest->setPayloadType(originalPayloadType);
+		memmove(dest->getBody(), getBody(), getBodySize(totalSize));
+		return totalSize;
+	}
+
+	[[nodiscard]] uint16_t getOriginalSeqNo() const {
+		return ntohs(*(uint16_t *)(header.getBody()));
+	}
+
+	char *getBody() { return header.getBody() + sizeof(uint16_t); }
+
+	size_t getBodySize(size_t totalSize) { return totalSize - ((char *)getBody() - (char *)this); }
+
+	RTP &getHeader() { return header; }
+
+	size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
+		header.setSeqNumber(getOriginalSeqNo());
+		header.setSsrc(originalSSRC);
+		header.setPayloadType(originalPayloadType);
+		// TODO, the -12 is the size of the header (which is variable!)
+		memmove(header.getBody(), header.getBody() + sizeof(uint16_t),
+		        totalSize - 12 - sizeof(uint16_t));
+		return totalSize - sizeof(uint16_t);
+	}
+};
+
+#pragma pack(pop)
+
+}; // namespace rtc
+
+#endif

+ 4 - 1
include/rtc/track.hpp

@@ -56,9 +56,13 @@ public:
 	// Extended API
 	// Extended API
 	size_t availableAmount() const override;
 	size_t availableAmount() const override;
 	std::optional<message_variant> receive() override;
 	std::optional<message_variant> receive() override;
+	std::optional<message_variant> peek() override;
+
+	bool requestKeyframe();
 
 
 	// RTCP handler
 	// RTCP handler
 	void setRtcpHandler(std::shared_ptr<RtcpHandler> handler);
 	void setRtcpHandler(std::shared_ptr<RtcpHandler> handler);
+	std::shared_ptr<RtcpHandler> getRtcpHandler();
 
 
 private:
 private:
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
@@ -81,4 +85,3 @@ private:
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif
-

+ 1 - 0
include/rtc/websocket.hpp

@@ -66,6 +66,7 @@ public:
 
 
 	// Extended API
 	// Extended API
 	std::optional<message_variant> receive() override;
 	std::optional<message_variant> receive() override;
+	std::optional<message_variant> peek() override;
 	size_t availableAmount() const override; // total size available to receive
 	size_t availableAmount() const override; // total size available to receive
 
 
 private:
 private:

+ 1 - 2
src/base64.cpp

@@ -25,8 +25,7 @@ namespace rtc {
 using std::to_integer;
 using std::to_integer;
 
 
 string to_base64(const binary &data) {
 string to_base64(const binary &data) {
-	static const char tab[] =
-	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	static const char tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 
 	string out;
 	string out;
 	out.reserve(3 * ((data.size() + 3) / 4));
 	out.reserve(3 * ((data.size() + 3) / 4));

+ 29 - 24
src/candidate.cpp

@@ -28,8 +28,8 @@
 #include <ws2tcpip.h>
 #include <ws2tcpip.h>
 #else
 #else
 #include <netdb.h>
 #include <netdb.h>
-#include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
+#include <sys/socket.h>
 #endif
 #endif
 
 
 #include <sys/types.h>
 #include <sys/types.h>
@@ -48,23 +48,31 @@ inline bool hasprefix(const string &str, const string &prefix) {
 
 
 namespace rtc {
 namespace rtc {
 
 
-Candidate::Candidate(string candidate, string mid)
+Candidate::Candidate()
     : mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
     : mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
-      mPort(0), mPriority(0) {
+      mPort(0), mPriority(0) {}
 
 
-	if (!candidate.empty()) {
-		const std::array prefixes{"a=", "candidate:"};
-		for (const string &prefix : prefixes)
-			if (hasprefix(candidate, prefix))
-				candidate.erase(0, prefix.size());
-	}
+Candidate::Candidate(string candidate) : Candidate() {
+	const std::array prefixes{"a=", "candidate:"};
+	for (const string &prefix : prefixes)
+		if (hasprefix(candidate, prefix))
+			candidate.erase(0, prefix.size());
 
 
 	mCandidate = std::move(candidate);
 	mCandidate = std::move(candidate);
-	mMid = std::move(mid);
+}
+
+Candidate::Candidate(string candidate, string mid) : Candidate(std::move(candidate)) {
+	if (!mid.empty())
+		mMid.emplace(std::move(mid));
+}
+
+void Candidate::hintMid(string mid) {
+	if (!mMid)
+		mMid.emplace(std::move(mid));
 }
 }
 
 
 bool Candidate::resolve(ResolveMode mode) {
 bool Candidate::resolve(ResolveMode mode) {
-	using TypeMap_t = std::unordered_map<string, Type>;	
+	using TypeMap_t = std::unordered_map<string, Type>;
 	using TcpTypeMap_t = std::unordered_map<string, TransportType>;
 	using TcpTypeMap_t = std::unordered_map<string, TransportType>;
 
 
 	static const TypeMap_t TypeMap = {{"host", Type::Host},
 	static const TypeMap_t TypeMap = {{"host", Type::Host},
@@ -79,12 +87,11 @@ bool Candidate::resolve(ResolveMode mode) {
 	if (mFamily != Family::Unresolved)
 	if (mFamily != Family::Unresolved)
 		return true;
 		return true;
 
 
-	if(mCandidate.empty())
+	if (mCandidate.empty())
 		throw std::logic_error("Candidate is empty");
 		throw std::logic_error("Candidate is empty");
 
 
 	PLOG_VERBOSE << "Resolving candidate (mode="
 	PLOG_VERBOSE << "Resolving candidate (mode="
-				 << (mode == ResolveMode::Simple ? "simple" : "lookup")
-				 << "): " << mCandidate;
+	             << (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mCandidate;
 
 
 	// See RFC 8445 for format
 	// See RFC 8445 for format
 	std::istringstream iss(mCandidate);
 	std::istringstream iss(mCandidate);
@@ -100,17 +107,16 @@ bool Candidate::resolve(ResolveMode mode) {
 			mType = it->second;
 			mType = it->second;
 		else
 		else
 			mType = Type::Unknown;
 			mType = Type::Unknown;
-		
+
 		if (transport == "UDP" || transport == "udp") {
 		if (transport == "UDP" || transport == "udp") {
 			mTransportType = TransportType::Udp;
 			mTransportType = TransportType::Udp;
-		}
-		else if (transport == "TCP" || transport == "tcp") {
+		} else if (transport == "TCP" || transport == "tcp") {
 			std::istringstream iss(left);
 			std::istringstream iss(left);
 			string tcptype_, tcptype;
 			string tcptype_, tcptype;
-			if(iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
+			if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
 				if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
 				if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
 					mTransportType = it->second;
 					mTransportType = it->second;
-				else 
+				else
 					mTransportType = TransportType::TcpUnknown;
 					mTransportType = TransportType::TcpUnknown;
 
 
 			} else {
 			} else {
@@ -127,8 +133,7 @@ bool Candidate::resolve(ResolveMode mode) {
 		if (mTransportType == TransportType::Udp) {
 		if (mTransportType == TransportType::Udp) {
 			hints.ai_socktype = SOCK_DGRAM;
 			hints.ai_socktype = SOCK_DGRAM;
 			hints.ai_protocol = IPPROTO_UDP;
 			hints.ai_protocol = IPPROTO_UDP;
-		}
-		else if (mTransportType != TransportType::Unknown) {
+		} else if (mTransportType != TransportType::Unknown) {
 			hints.ai_socktype = SOCK_STREAM;
 			hints.ai_socktype = SOCK_STREAM;
 			hints.ai_protocol = IPPROTO_TCP;
 			hints.ai_protocol = IPPROTO_TCP;
 		}
 		}
@@ -146,11 +151,11 @@ bool Candidate::resolve(ResolveMode mode) {
 					if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
 					if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
 					                MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
 					                MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
 					                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
 					                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
-						
+
 						mAddress = nodebuffer;
 						mAddress = nodebuffer;
 						mPort = uint16_t(std::stoul(servbuffer));
 						mPort = uint16_t(std::stoul(servbuffer));
 						mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
 						mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
-						
+
 						const char sp{' '};
 						const char sp{' '};
 						std::ostringstream oss;
 						std::ostringstream oss;
 						oss << foundation << sp << component << sp << transport << sp << priority;
 						oss << foundation << sp << component << sp << transport << sp << priority;
@@ -173,7 +178,7 @@ bool Candidate::resolve(ResolveMode mode) {
 
 
 string Candidate::candidate() const { return "candidate:" + mCandidate; }
 string Candidate::candidate() const { return "candidate:" + mCandidate; }
 
 
-string Candidate::mid() const { return mMid; }
+string Candidate::mid() const { return mMid.value_or("0"); }
 
 
 Candidate::operator string() const {
 Candidate::operator string() const {
 	std::ostringstream line;
 	std::ostringstream line;

+ 127 - 178
src/capi.cpp

@@ -195,6 +195,31 @@ template <typename F> int wrap(F func) {
 		return RTC_ERR_SUCCESS;                                                                    \
 		return RTC_ERR_SUCCESS;                                                                    \
 	})
 	})
 
 
+int copyAndReturn(string s, char *buffer, int size) {
+	if (!buffer)
+		return int(s.size() + 1);
+
+	if (size < int(s.size()))
+		return RTC_ERR_TOO_SMALL;
+
+	std::copy(s.begin(), s.end(), buffer);
+	buffer[s.size()] = '\0';
+	return int(s.size() + 1);
+}
+
+int copyAndReturn(binary b, char *buffer, int size) {
+	if (!buffer)
+		return int(b.size());
+
+	if (size < int(b.size()))
+		return RTC_ERR_TOO_SMALL;
+
+	auto data = reinterpret_cast<const char *>(b.data());
+	std::copy(data, data + b.size(), buffer);
+	buffer[b.size()] = '\0';
+	return int(b.size());
+}
+
 class plogAppender : public plog::IAppender {
 class plogAppender : public plog::IAppender {
 public:
 public:
 	plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
 	plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
@@ -279,44 +304,48 @@ int rtcDeletePeerConnection(int pc) {
 	});
 	});
 }
 }
 
 
-int rtcAddDataChannel(int pc, const char *label) {
-	return rtcAddDataChannelExt(pc, label, nullptr, nullptr);
-}
+int rtcAddDataChannel(int pc, const char *label) { return rtcAddDataChannelEx(pc, label, nullptr); }
 
 
-int rtcAddDataChannelExt(int pc, const char *label, const char *protocol,
-                         const rtcReliability *reliability) {
+int rtcAddDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
 	return WRAP({
 	return WRAP({
-		Reliability r = {};
-		if (reliability) {
-			r.unordered = reliability->unordered;
+		DataChannelInit dci = {};
+		if (init) {
+			auto *reliability = &init->reliability;
+			dci.reliability.unordered = reliability->unordered;
 			if (reliability->unreliable) {
 			if (reliability->unreliable) {
 				if (reliability->maxPacketLifeTime > 0) {
 				if (reliability->maxPacketLifeTime > 0) {
-					r.type = Reliability::Type::Timed;
-					r.rexmit = milliseconds(reliability->maxPacketLifeTime);
+					dci.reliability.type = Reliability::Type::Timed;
+					dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
 				} else {
 				} else {
-					r.type = Reliability::Type::Rexmit;
-					r.rexmit = int(reliability->maxRetransmits);
+					dci.reliability.type = Reliability::Type::Rexmit;
+					dci.reliability.rexmit = int(reliability->maxRetransmits);
 				}
 				}
 			} else {
 			} else {
-				r.type = Reliability::Type::Reliable;
+				dci.reliability.type = Reliability::Type::Reliable;
 			}
 			}
+
+			dci.negotiated = init->negotiated;
+			dci.id = init->manualStream ? std::make_optional(init->stream) : nullopt;
+			dci.protocol = init->protocol ? init->protocol : "";
 		}
 		}
+
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
-		int dc = emplaceDataChannel(peerConnection->addDataChannel(
-		    string(label ? label : ""), string(protocol ? protocol : ""), r));
+		int dc = emplaceDataChannel(
+		    peerConnection->addDataChannel(string(label ? label : ""), std::move(dci)));
+
 		if (auto ptr = getUserPointer(pc))
 		if (auto ptr = getUserPointer(pc))
 			rtcSetUserPointer(dc, *ptr);
 			rtcSetUserPointer(dc, *ptr);
+
 		return dc;
 		return dc;
 	});
 	});
 }
 }
 
 
 int rtcCreateDataChannel(int pc, const char *label) {
 int rtcCreateDataChannel(int pc, const char *label) {
-	return rtcCreateDataChannelExt(pc, label, nullptr, nullptr);
+	return rtcCreateDataChannelEx(pc, label, nullptr);
 }
 }
 
 
-int rtcCreateDataChannelExt(int pc, const char *label, const char *protocol,
-                            const rtcReliability *reliability) {
-	int dc = rtcAddDataChannelExt(pc, label, protocol, reliability);
+int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init) {
+	int dc = rtcAddDataChannelEx(pc, label, init);
 	rtcSetLocalDescription(pc, NULL);
 	rtcSetLocalDescription(pc, NULL);
 	return dc;
 	return dc;
 }
 }
@@ -345,6 +374,7 @@ int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
 		int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
 		int tr = emplaceTrack(peerConnection->addTrack(std::move(media)));
 		if (auto ptr = getUserPointer(pc))
 		if (auto ptr = getUserPointer(pc))
 			rtcSetUserPointer(tr, *ptr);
 			rtcSetUserPointer(tr, *ptr);
+
 		return tr;
 		return tr;
 	});
 	});
 }
 }
@@ -366,19 +396,7 @@ int rtcDeleteTrack(int tr) {
 int rtcGetTrackDescription(int tr, char *buffer, int size) {
 int rtcGetTrackDescription(int tr, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
-
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		string description(track->description());
-		const char *data = description.data();
-		size = std::min(size - 1, int(description.size()));
-		std::copy(data, data + size, buffer);
-		buffer[size] = '\0';
-		return int(size + 1);
+		return copyAndReturn(track->description(), buffer, size);
 	});
 	});
 }
 }
 
 
@@ -547,22 +565,10 @@ int rtcGetLocalDescription(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		if (auto desc = peerConnection->localDescription()) {
-			auto sdp = string(*desc);
-			const char *data = sdp.data();
-			size = std::min(size - 1, int(sdp.size()));
-			std::copy(data, data + size, buffer);
-			buffer[size] = '\0';
-			return size + 1;
-		}
-
-		return RTC_ERR_FAILURE;
+		if (auto desc = peerConnection->localDescription())
+			return copyAndReturn(string(*desc), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
 	});
 	});
 }
 }
 
 
@@ -570,22 +576,10 @@ int rtcGetRemoteDescription(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		if (auto desc = peerConnection->remoteDescription()) {
-			auto sdp = string(*desc);
-			const char *data = sdp.data();
-			size = std::min(size - 1, int(sdp.size()));
-			std::copy(data, data + size, buffer);
-			buffer[size] = '\0';
-			return size + 1;
-		}
-
-		return RTC_ERR_FAILURE;
+		if (auto desc = peerConnection->remoteDescription())
+			return copyAndReturn(string(*desc), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
 	});
 	});
 }
 }
 
 
@@ -593,21 +587,10 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		if (auto addr = peerConnection->localAddress()) {
-			const char *data = addr->data();
-			size = std::min(size - 1, int(addr->size()));
-			std::copy(data, data + size, buffer);
-			buffer[size] = '\0';
-			return size + 1;
-		}
-
-		return RTC_ERR_FAILURE;
+		if (auto addr = peerConnection->localAddress())
+			return copyAndReturn(std::move(*addr), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
 	});
 	});
 }
 }
 
 
@@ -615,21 +598,10 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		if (auto addr = peerConnection->remoteAddress()) {
-			const char *data = addr->data();
-			size = std::min(size - 1, int(addr->size()));
-			std::copy(data, data + size, buffer);
-			buffer[size] = '\0';
-			return int(size + 1);
-		}
-
-		return RTC_ERR_FAILURE;
+		if (auto addr = peerConnection->remoteAddress())
+			return copyAndReturn(std::move(*addr), buffer, size);
+		else
+			return RTC_ERR_NOT_AVAIL;
 	});
 	});
 }
 }
 
 
@@ -637,68 +609,41 @@ int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (!local)
-			localSize = 0;
-		if (!remote)
-			remoteSize = 0;
-
 		Candidate localCand;
 		Candidate localCand;
 		Candidate remoteCand;
 		Candidate remoteCand;
-		if (peerConnection->getSelectedCandidatePair(&localCand, &remoteCand)) {
-			if (localSize > 0) {
-				string localSdp = string(localCand);
-				localSize = std::min(localSize - 1, int(localSdp.size()));
-				std::copy(localSdp.begin(), localSdp.begin() + localSize, local);
-				local[localSize] = '\0';
-			}
-			if (remoteSize > 0) {
-				string remoteSdp = string(remoteCand);
-				remoteSize = std::min(remoteSize - 1, int(remoteSdp.size()));
-				std::copy(remoteSdp.begin(), remoteSdp.begin() + remoteSize, remote);
-				remote[remoteSize] = '\0';
-			}
-			return localSize + remoteSize;
-		}
+		if (!peerConnection->getSelectedCandidatePair(&localCand, &remoteCand))
+			return RTC_ERR_NOT_AVAIL;
 
 
-		return RTC_ERR_FAILURE;
+		int localRet = copyAndReturn(string(localCand), local, localSize);
+		if (localRet < 0)
+			return localRet;
+
+		int remoteRet = copyAndReturn(string(remoteCand), remote, remoteSize);
+		if (remoteRet < 0)
+			return remoteRet;
+
+		return std::max(localRet, remoteRet);
 	});
 	});
 }
 }
 
 
-int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
+int rtcGetDataChannelStream(int dc) {
 	return WRAP({
 	return WRAP({
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
+		return int(dataChannel->id());
+	});
+}
 
 
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		string label = dataChannel->label();
-		const char *data = label.data();
-		size = std::min(size - 1, int(label.size()));
-		std::copy(data, data + size, buffer);
-		buffer[size] = '\0';
-		return int(size + 1);
+int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
+	return WRAP({
+		auto dataChannel = getDataChannel(dc);
+		return copyAndReturn(dataChannel->label(), buffer, size);
 	});
 	});
 }
 }
 
 
 int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
 int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
-
-		if (size <= 0)
-			return 0;
-
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		string protocol = dataChannel->protocol();
-		const char *data = protocol.data();
-		size = std::min(size - 1, int(protocol.size()));
-		std::copy(data, data + size, buffer);
-		buffer[size] = '\0';
-		return int(size + 1);
+		return copyAndReturn(dataChannel->protocol(), buffer, size);
 	});
 	});
 }
 }
 
 
@@ -709,19 +654,19 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 		if (!reliability)
 		if (!reliability)
 			throw std::invalid_argument("Unexpected null pointer for reliability");
 			throw std::invalid_argument("Unexpected null pointer for reliability");
 
 
-		Reliability r = dataChannel->reliability();
+		Reliability dcr = dataChannel->reliability();
 		std::memset(reliability, 0, sizeof(*reliability));
 		std::memset(reliability, 0, sizeof(*reliability));
-		reliability->unordered = r.unordered;
-		if (r.type == Reliability::Type::Timed) {
+		reliability->unordered = dcr.unordered;
+		if (dcr.type == Reliability::Type::Timed) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxPacketLifeTime = unsigned(std::get<milliseconds>(r.rexmit).count());
-		} else if (r.type == Reliability::Type::Rexmit) {
+			reliability->maxPacketLifeTime = unsigned(std::get<milliseconds>(dcr.rexmit).count());
+		} else if (dcr.type == Reliability::Type::Rexmit) {
 			reliability->unreliable = true;
 			reliability->unreliable = true;
-			reliability->maxRetransmits = unsigned(std::get<int>(r.rexmit));
+			reliability->maxRetransmits = unsigned(std::get<int>(dcr.rexmit));
 		} else {
 		} else {
 			reliability->unreliable = false;
 			reliability->unreliable = false;
 		}
 		}
-		return 0;
+		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }
 
 
@@ -837,12 +782,12 @@ int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
 	return WRAP({
 	return WRAP({
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 		if (cb)
 		if (cb)
-			channel->onOpen([id, cb]() {
+			channel->onAvailable([id, cb]() {
 				if (auto ptr = getUserPointer(id))
 				if (auto ptr = getUserPointer(id))
 					cb(id, *ptr);
 					cb(id, *ptr);
 			});
 			});
 		else
 		else
-			channel->onOpen(nullptr);
+			channel->onAvailable(nullptr);
 	});
 	});
 }
 }
 
 
@@ -853,34 +798,38 @@ int rtcReceiveMessage(int id, char *buffer, int *size) {
 		if (!size)
 		if (!size)
 			throw std::invalid_argument("Unexpected null pointer for size");
 			throw std::invalid_argument("Unexpected null pointer for size");
 
 
-		if (!buffer && *size != 0)
-			throw std::invalid_argument("Unexpected null pointer for buffer");
-
-		if (auto message = channel->receive())
-			return std::visit( //
-			    overloaded{    //
-			               [&](binary b) {
-				               if (*size > 0) {
-					               *size = std::min(*size, int(b.size()));
-					               auto data = reinterpret_cast<const char *>(b.data());
-					               std::copy(data, data + *size, buffer);
-				               }
-				               return 1;
-			               },
-			               [&](string s) {
-				               if (*size > 0) {
-					               int len = std::min(*size - 1, int(s.size()));
-					               if (len >= 0) {
-						               std::copy(s.data(), s.data() + len, buffer);
-						               buffer[len] = '\0';
-					               }
-					               *size = -(len + 1);
-				               }
-				               return 1;
-			               }},
-			    *message);
-		else
-			return 0;
+		*size = std::abs(*size);
+
+		auto message = channel->peek();
+		if (!message)
+			return RTC_ERR_NOT_AVAIL;
+
+		return std::visit( //
+		    overloaded{
+		        [&](binary b) {
+			        int ret = copyAndReturn(std::move(b), buffer, *size);
+			        if (ret >= 0) {
+				        channel->receive(); // discard
+				        *size = ret;
+				        return RTC_ERR_SUCCESS;
+			        } else {
+				        *size = int(b.size());
+				        return ret;
+			        }
+		        },
+		        [&](string s) {
+			        int ret = copyAndReturn(std::move(s), buffer, *size);
+			        if (ret >= 0) {
+				        channel->receive(); // discard
+				        *size = -ret;
+				        return RTC_ERR_SUCCESS;
+			        } else {
+				        *size = -int(s.size() + 1);
+				        return ret;
+			        }
+		        },
+		    },
+		    *message);
 	});
 	});
 }
 }
 
 

+ 11 - 10
src/certificate.cpp

@@ -153,22 +153,23 @@ Certificate::Certificate(string crt_pem, string key_pem) {
 	mFingerprint = make_fingerprint(mX509.get());
 	mFingerprint = make_fingerprint(mX509.get());
 }
 }
 
 
-Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey) :
-	mX509(std::move(x509)), mPKey(std::move(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());
 	mFingerprint = make_fingerprint(mX509.get());
 }
 }
 
 
 string Certificate::fingerprint() const { return mFingerprint; }
 string Certificate::fingerprint() const { return mFingerprint; }
 
 
-std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const { return {mX509.get(), mPKey.get()}; }
+std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
+	return {mX509.get(), mPKey.get()};
+}
 
 
 string make_fingerprint(X509 *x509) {
 string make_fingerprint(X509 *x509) {
 	const size_t size = 32;
 	const size_t size = 32;
-    unsigned char buffer[size];
-    unsigned int len = size;
-    if (!X509_digest(x509, EVP_sha256(), buffer, &len))
-      throw std::runtime_error("X509 fingerprint error");
+	unsigned char buffer[size];
+	unsigned int len = size;
+	if (!X509_digest(x509, EVP_sha256(), buffer, &len))
+		throw std::runtime_error("X509 fingerprint error");
 
 
 	std::ostringstream oss;
 	std::ostringstream oss;
 	oss << std::hex << std::uppercase << std::setfill('0');
 	oss << std::hex << std::uppercase << std::setfill('0');
@@ -186,10 +187,10 @@ certificate_ptr make_certificate_impl(string commonName) {
 	shared_ptr<X509> x509(X509_new(), X509_free);
 	shared_ptr<X509> x509(X509_new(), X509_free);
 	shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
 	shared_ptr<EVP_PKEY> 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);
 	unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
 	unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
 	unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
 	unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
-    unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);
+	unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);
 
 
 	if (!x509 || !pkey || !rsa || !exponent || !serial_number || !name)
 	if (!x509 || !pkey || !rsa || !exponent || !serial_number || !name)
 		throw std::runtime_error("Unable allocate structures for certificate generation");
 		throw std::runtime_error("Unable allocate structures for certificate generation");

+ 3 - 10
src/channel.cpp

@@ -26,13 +26,9 @@ size_t Channel::bufferedAmount() const { return mBufferedAmount; }
 
 
 size_t Channel::availableAmount() const { return 0; }
 size_t Channel::availableAmount() const { return 0; }
 
 
-void Channel::onOpen(std::function<void()> callback) {
-	mOpenCallback = callback;
-}
+void Channel::onOpen(std::function<void()> callback) { mOpenCallback = callback; }
 
 
-void Channel::onClosed(std::function<void()> callback) {
-	mClosedCallback = callback;
-}
+void Channel::onClosed(std::function<void()> callback) { mClosedCallback = callback; }
 
 
 void Channel::onError(std::function<void(string error)> callback) { mErrorCallback = callback; }
 void Channel::onError(std::function<void(string error)> callback) { mErrorCallback = callback; }
 
 
@@ -57,9 +53,7 @@ void Channel::onBufferedAmountLow(std::function<void()> callback) {
 
 
 void Channel::setBufferedAmountLowThreshold(size_t amount) { mBufferedAmountLowThreshold = amount; }
 void Channel::setBufferedAmountLowThreshold(size_t amount) { mBufferedAmountLowThreshold = amount; }
 
 
-void Channel::onAvailable(std::function<void()> callback) {
-	mAvailableCallback = callback;
-}
+void Channel::onAvailable(std::function<void()> callback) { mAvailableCallback = callback; }
 
 
 void Channel::triggerOpen() { mOpenCallback(); }
 void Channel::triggerOpen() { mOpenCallback(); }
 
 
@@ -96,4 +90,3 @@ void Channel::resetCallbacks() {
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc
-

+ 89 - 56
src/datachannel.cpp

@@ -72,24 +72,18 @@ struct CloseMessage {
 };
 };
 #pragma pack(pop)
 #pragma pack(pop)
 
 
-DataChannel::DataChannel(weak_ptr<PeerConnection> pc, unsigned int stream, string label,
+DataChannel::DataChannel(weak_ptr<PeerConnection> pc, uint16_t stream, string label,
                          string protocol, Reliability reliability)
                          string protocol, Reliability reliability)
     : mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
     : mPeerConnection(pc), mStream(stream), mLabel(std::move(label)),
       mProtocol(std::move(protocol)),
       mProtocol(std::move(protocol)),
       mReliability(std::make_shared<Reliability>(std::move(reliability))),
       mReliability(std::make_shared<Reliability>(std::move(reliability))),
       mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
       mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
 
 
-DataChannel::DataChannel(weak_ptr<PeerConnection> pc, weak_ptr<SctpTransport> transport,
-                         unsigned int stream)
-    : mPeerConnection(pc), mSctpTransport(transport), mStream(stream),
-      mReliability(std::make_shared<Reliability>()),
-      mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
+DataChannel::~DataChannel() { close(); }
 
 
-DataChannel::~DataChannel() {
-	close();
-}
+uint16_t DataChannel::stream() const { return mStream; }
 
 
-unsigned int DataChannel::stream() const { return mStream; }
+uint16_t DataChannel::id() const { return uint16_t(mStream); }
 
 
 string DataChannel::label() const { return mLabel; }
 string DataChannel::label() const { return mLabel; }
 
 
@@ -123,14 +117,29 @@ bool DataChannel::send(const byte *data, size_t size) {
 
 
 std::optional<message_variant> DataChannel::receive() {
 std::optional<message_variant> DataChannel::receive() {
 	while (auto next = mRecvQueue.tryPop()) {
 	while (auto next = mRecvQueue.tryPop()) {
-		message_ptr message = std::move(*next);
-		if (message->type == Message::Control) {
-			auto raw = reinterpret_cast<const uint8_t *>(message->data());
-			if (!message->empty() && raw[0] == MESSAGE_CLOSE)
-				remoteClose();
-		} else {
+		message_ptr message = *next;
+		if (message->type != Message::Control)
 			return to_variant(std::move(*message));
 			return to_variant(std::move(*message));
-		}
+
+		auto raw = reinterpret_cast<const uint8_t *>(message->data());
+		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
+			remoteClose();
+	}
+
+	return nullopt;
+}
+
+std::optional<message_variant> DataChannel::peek() {
+	while (auto next = mRecvQueue.peek()) {
+		message_ptr message = *next;
+		if (message->type != Message::Control)
+			return to_variant(std::move(*message));
+
+		auto raw = reinterpret_cast<const uint8_t *>(message->data());
+		if (!message->empty() && raw[0] == MESSAGE_CLOSE)
+			remoteClose();
+
+		mRecvQueue.tryPop();
 	}
 	}
 
 
 	return nullopt;
 	return nullopt;
@@ -156,43 +165,12 @@ size_t DataChannel::availableAmount() const { return mRecvQueue.amount(); }
 void DataChannel::open(shared_ptr<SctpTransport> transport) {
 void DataChannel::open(shared_ptr<SctpTransport> transport) {
 	mSctpTransport = transport;
 	mSctpTransport = transport;
 
 
-	uint8_t channelType;
-	uint32_t reliabilityParameter;
-	switch (mReliability->type) {
-	case Reliability::Type::Rexmit:
-		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
-		reliabilityParameter = uint32_t(std::get<int>(mReliability->rexmit));
-		break;
-
-	case Reliability::Type::Timed:
-		channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
-		reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
-		break;
-
-	default:
-		channelType = CHANNEL_RELIABLE;
-		reliabilityParameter = 0;
-		break;
-	}
-
-	if (mReliability->unordered)
-		channelType |= 0x80;
-
-	const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
-	binary buffer(len, byte(0));
-	auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
-	open.type = MESSAGE_OPEN;
-	open.channelType = channelType;
-	open.priority = htons(0);
-	open.reliabilityParameter = htonl(reliabilityParameter);
-	open.labelLength = htons(uint16_t(mLabel.size()));
-	open.protocolLength = htons(uint16_t(mProtocol.size()));
-
-	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
-	std::copy(mLabel.begin(), mLabel.end(), end);
-	std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
+	if (!mIsOpen.exchange(true))
+		triggerOpen();
+}
 
 
-	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
+void DataChannel::processOpenMessage(message_ptr) {
+	PLOG_WARNING << "Received an open message for a user-negotiated DataChannel, ignoring";
 }
 }
 
 
 bool DataChannel::outgoing(message_ptr message) {
 bool DataChannel::outgoing(message_ptr message) {
@@ -252,7 +230,62 @@ void DataChannel::incoming(message_ptr message) {
 	}
 	}
 }
 }
 
 
-void DataChannel::processOpenMessage(message_ptr message) {
+NegociatedDataChannel::NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream,
+                                             string label, string protocol, Reliability reliability)
+    : DataChannel(pc, stream, std::move(label), std::move(protocol), std::move(reliability)) {}
+
+NegociatedDataChannel::NegociatedDataChannel(std::weak_ptr<PeerConnection> pc,
+                                             std::weak_ptr<SctpTransport> transport,
+                                             uint16_t stream)
+    : DataChannel(pc, stream, "", "", {}) {
+	mSctpTransport = transport;
+}
+
+NegociatedDataChannel::~NegociatedDataChannel() {}
+
+void NegociatedDataChannel::open(shared_ptr<SctpTransport> transport) {
+	mSctpTransport = transport;
+
+	uint8_t channelType;
+	uint32_t reliabilityParameter;
+	switch (mReliability->type) {
+	case Reliability::Type::Rexmit:
+		channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
+		reliabilityParameter = uint32_t(std::get<int>(mReliability->rexmit));
+		break;
+
+	case Reliability::Type::Timed:
+		channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
+		reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
+		break;
+
+	default:
+		channelType = CHANNEL_RELIABLE;
+		reliabilityParameter = 0;
+		break;
+	}
+
+	if (mReliability->unordered)
+		channelType |= 0x80;
+
+	const size_t len = sizeof(OpenMessage) + mLabel.size() + mProtocol.size();
+	binary buffer(len, byte(0));
+	auto &open = *reinterpret_cast<OpenMessage *>(buffer.data());
+	open.type = MESSAGE_OPEN;
+	open.channelType = channelType;
+	open.priority = htons(0);
+	open.reliabilityParameter = htonl(reliabilityParameter);
+	open.labelLength = htons(uint16_t(mLabel.size()));
+	open.protocolLength = htons(uint16_t(mProtocol.size()));
+
+	auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
+	std::copy(mLabel.begin(), mLabel.end(), end);
+	std::copy(mProtocol.begin(), mProtocol.end(), end + mLabel.size());
+
+	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
+}
+
+void NegociatedDataChannel::processOpenMessage(message_ptr message) {
 	auto transport = mSctpTransport.lock();
 	auto transport = mSctpTransport.lock();
 	if (!transport)
 	if (!transport)
 		throw std::runtime_error("DataChannel has no transport");
 		throw std::runtime_error("DataChannel has no transport");
@@ -294,8 +327,8 @@ void DataChannel::processOpenMessage(message_ptr message) {
 
 
 	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
 	transport->send(make_message(buffer.begin(), buffer.end(), Message::Control, mStream));
 
 
-	mIsOpen = true;
-	triggerOpen();
+	if (!mIsOpen.exchange(true))
+		triggerOpen();
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc

+ 160 - 48
src/description.cpp

@@ -1,6 +1,6 @@
 /**
 /**
  * Copyright (c) 2019-2020 Paul-Louis Ageneau
  * Copyright (c) 2019-2020 Paul-Louis Ageneau
- * Copyright (c) 2020 Staz M
+ * Copyright (c) 2020 Staz Modrzynski
  *
  *
  * This library is free software; you can redistribute it and/or
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * modify it under the terms of the GNU Lesser General Public
@@ -66,11 +66,6 @@ template <typename T> T to_integer(string_view s) {
 
 
 namespace rtc {
 namespace rtc {
 
 
-Description::Description(const string &sdp, const string &typeString)
-    : Description(sdp, stringToType(typeString)) {}
-
-Description::Description(const string &sdp, Type type) : Description(sdp, type, Role::ActPass) {}
-
 Description::Description(const string &sdp, Type type, Role role)
 Description::Description(const string &sdp, Type type, Role role)
     : mType(Type::Unspec), mRole(role) {
     : mType(Type::Unspec), mRole(role) {
 	hintType(type);
 	hintType(type);
@@ -141,6 +136,10 @@ Description::Description(const string &sdp, Type type, Role role)
 	}
 	}
 }
 }
 
 
+Description::Description(const string &sdp, string typeString)
+    : Description(sdp, !typeString.empty() ? stringToType(typeString) : Type::Unspec,
+                  Role::ActPass) {}
+
 Description::Type Description::type() const { return mType; }
 Description::Type Description::type() const { return mType; }
 
 
 string Description::typeString() const { return typeToString(mType); }
 string Description::typeString() const { return typeToString(mType); }
@@ -173,12 +172,15 @@ void Description::setFingerprint(string fingerprint) {
 }
 }
 
 
 void Description::addCandidate(Candidate candidate) {
 void Description::addCandidate(Candidate candidate) {
+	candidate.hintMid(bundleMid());
 	mCandidates.emplace_back(std::move(candidate));
 	mCandidates.emplace_back(std::move(candidate));
 }
 }
 
 
 void Description::addCandidates(std::vector<Candidate> candidates) {
 void Description::addCandidates(std::vector<Candidate> candidates) {
-	for(auto candidate : candidates)
+	for (Candidate candidate : candidates) {
+		candidate.hintMid(bundleMid());
 		mCandidates.emplace_back(std::move(candidate));
 		mCandidates.emplace_back(std::move(candidate));
+	}
 }
 }
 
 
 void Description::endCandidates() { mEnded = true; }
 void Description::endCandidates() { mEnded = true; }
@@ -383,8 +385,9 @@ int Description::addAudio(string mid, Direction dir) {
 	return addMedia(Audio(std::move(mid), dir));
 	return addMedia(Audio(std::move(mid), dir));
 }
 }
 
 
-std::variant<Description::Media *, Description::Application *> Description::media(int index) {
-	if (index < 0 || index >= int(mEntries.size()))
+std::variant<Description::Media *, Description::Application *>
+Description::media(unsigned int index) {
+	if (index >= mEntries.size())
 		throw std::out_of_range("Media index out of range");
 		throw std::out_of_range("Media index out of range");
 
 
 	const auto &entry = mEntries[index];
 	const auto &entry = mEntries[index];
@@ -402,8 +405,8 @@ std::variant<Description::Media *, Description::Application *> Description::medi
 }
 }
 
 
 std::variant<const Description::Media *, const Description::Application *>
 std::variant<const Description::Media *, const Description::Application *>
-Description::media(int index) const {
-	if (index < 0 || index >= int(mEntries.size()))
+Description::media(unsigned int index) const {
+	if (index >= mEntries.size())
 		throw std::out_of_range("Media index out of range");
 		throw std::out_of_range("Media index out of range");
 
 
 	const auto &entry = mEntries[index];
 	const auto &entry = mEntries[index];
@@ -420,7 +423,7 @@ Description::media(int index) const {
 	}
 	}
 }
 }
 
 
-int Description::mediaCount() const { return int(mEntries.size()); }
+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) {
@@ -468,8 +471,11 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 		break;
 		break;
 	}
 	}
 
 
-	for (const auto &attr : mAttributes)
-		sdp << "a=" << attr << eol;
+	for (const auto &attr : mAttributes) {
+		if (attr.find("extmap") == std::string::npos &&
+		    attr.find("rtcp-rsize") == std::string::npos)
+			sdp << "a=" << attr << eol;
+	}
 
 
 	return sdp.str();
 	return sdp.str();
 }
 }
@@ -495,6 +501,36 @@ void Description::Entry::parseSdpLine(string_view line) {
 			mAttributes.emplace_back(line.substr(2));
 			mAttributes.emplace_back(line.substr(2));
 	}
 	}
 }
 }
+std::vector<string>::iterator Description::Entry::beginAttributes() { return mAttributes.begin(); }
+std::vector<string>::iterator Description::Entry::endAttributes() { return mAttributes.end(); }
+std::vector<string>::iterator
+Description::Entry::removeAttribute(std::vector<string>::iterator it) {
+	return mAttributes.erase(it);
+}
+
+void Description::Media::addSSRC(uint32_t ssrc, std::string name) {
+	mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name);
+	mSsrcs.emplace_back(ssrc);
+}
+
+void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::string name) {
+	auto it = mAttributes.begin();
+	while (it != mAttributes.end()) {
+		if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
+			it = mAttributes.erase(it);
+		} else
+			it++;
+	}
+	mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name);
+}
+
+void Description::Media::addSSRC(uint32_t ssrc) {
+	mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
+}
+
+bool Description::Media::hasSSRC(uint32_t ssrc) {
+	return std::find(mSsrcs.begin(), mSsrcs.end(), ssrc) != mSsrcs.end();
+}
 
 
 Description::Application::Application(string mid)
 Description::Application::Application(string mid)
     : Entry("application 9 UDP/DTLS/SCTP", std::move(mid), Direction::SendRecv) {}
     : Entry("application 9 UDP/DTLS/SCTP", std::move(mid), Direction::SendRecv) {}
@@ -642,24 +678,57 @@ void Description::Media::removeFormat(const string &fmt) {
 	}
 	}
 }
 }
 
 
-void Description::Media::addVideoCodec(int payloadType, const string &codec) {
+void Description::Video::addVideoCodec(int payloadType, const string &codec) {
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
 	map.addFB("nack");
 	map.addFB("nack");
+	map.addFB("nack pli");
+	//    map.addFB("nack fir");
 	map.addFB("goog-remb");
 	map.addFB("goog-remb");
 	if (codec == "H264") {
 	if (codec == "H264") {
 		// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
 		// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
 		// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
 		// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
 		// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
 		// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
-		map.fmtps.emplace_back("profile-level-id=42E02A;level-asymmetry-allowed=1");
+		map.fmtps.emplace_back(
+		    "profile-level-id=4de01f;packetization-mode=1;level-asymmetry-allowed=1");
+
+		// Because certain Android devices don't like me, let us just negotiate some random
+		{
+			RTPMap map(std::to_string(payloadType + 1) + ' ' + codec + "/90000");
+			map.addFB("nack");
+			map.addFB("nack pli");
+			//            map.addFB("nack fir");
+			map.addFB("goog-remb");
+			addRTPMap(map);
+		}
 	}
 	}
-	mRtpMap.emplace(map.pt, map);
+	addRTPMap(map);
+
+	//	// RTX Packets
+	/* TODO
+	 *  TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
+	 * negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
+	 * don't negotitate it and (maybe) break RTX support with Edge. Two) we do negotiate it and
+	 * rebuild the original packet before we send it distribute it to each track. Three) we complain
+	 * to mozilla. This one probably won't do much.
+	 */
+	//    RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000");
+	//    // TODO rtx-time is how long can a request be stashed for before needing to resend it.
+	//    Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
+	//    ";rtx-time=3000"); addRTPMap(rtx);
+}
+
+void Description::Audio::addAudioCodec(int payloadType, const string &codec) {
+	// TODO This 48000/2 should be parameterized
+	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
+	map.fmtps.emplace_back("maxaveragebitrate=96000; stereo=1; sprop-stereo=1; useinbandfec=1");
+	addRTPMap(map);
 }
 }
 
 
-void Description::Media::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
+void Description::Video::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
 
 
-void Description::Media::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8"); }
+void Description::Video::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8"); }
 
 
-void Description::Media::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9"); }
+void Description::Video::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9"); }
 
 
 void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
 void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
 
 
@@ -686,8 +755,10 @@ string Description::Media::generateSdpLines(string_view eol) const {
 			sdp << '/' << map.encParams;
 			sdp << '/' << map.encParams;
 		sdp << eol;
 		sdp << eol;
 
 
-		for (const auto &val : map.rtcpFbs)
-			sdp << "a=rtcp-fb:" << map.pt << ' ' << val << eol;
+		for (const auto &val : map.rtcpFbs) {
+			if (val != "transport-cc")
+				sdp << "a=rtcp-fb:" << map.pt << ' ' << val << eol;
+		}
 		for (const auto &val : map.fmtps)
 		for (const auto &val : map.fmtps)
 			sdp << "a=fmtp:" << map.pt << ' ' << val << eol;
 			sdp << "a=fmtp:" << map.pt << ' ' << val << eol;
 	}
 	}
@@ -701,29 +772,32 @@ void Description::Media::parseSdpLine(string_view line) {
 		auto [key, value] = parse_pair(attr);
 		auto [key, value] = parse_pair(attr);
 
 
 		if (key == "rtpmap") {
 		if (key == "rtpmap") {
-			Description::Media::RTPMap map(value);
-			int pt = map.pt;
-			mRtpMap.emplace(pt, std::move(map));
+			auto pt = Description::Media::RTPMap::parsePT(value);
+			auto it = mRtpMap.find(pt);
+			if (it == mRtpMap.end()) {
+				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap(value))).first;
+			} else {
+				it->second.setMLine(value);
+			}
 		} else if (key == "rtcp-fb") {
 		} else if (key == "rtcp-fb") {
 			size_t p = value.find(' ');
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			int pt = to_integer<int>(value.substr(0, p));
 			auto it = mRtpMap.find(pt);
 			auto it = mRtpMap.find(pt);
 			if (it == mRtpMap.end()) {
 			if (it == mRtpMap.end()) {
-				PLOG_WARNING << "rtcp-fb applied before the corresponding rtpmap, ignoring";
-			} else {
-				it->second.rtcpFbs.emplace_back(value.substr(p + 1));
+				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
 			}
 			}
+			it->second.rtcpFbs.emplace_back(value.substr(p + 1));
 		} else if (key == "fmtp") {
 		} else if (key == "fmtp") {
 			size_t p = value.find(' ');
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			int pt = to_integer<int>(value.substr(0, p));
 			auto it = mRtpMap.find(pt);
 			auto it = mRtpMap.find(pt);
-			if (it == mRtpMap.end()) {
-				PLOG_WARNING << "fmtp applied before the corresponding rtpmap, ignoring";
-			} else {
-				it->second.fmtps.emplace_back(value.substr(p + 1));
-			}
+			if (it == mRtpMap.end())
+				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
+			it->second.fmtps.emplace_back(value.substr(p + 1));
 		} else if (key == "rtcp-mux") {
 		} else if (key == "rtcp-mux") {
 			// always added
 			// always added
+		} else if (key == "ssrc") {
+			mSsrcs.emplace_back(std::stoul((std::string)value));
 		} else {
 		} else {
 			Entry::parseSdpLine(line);
 			Entry::parseSdpLine(line);
 		}
 		}
@@ -734,7 +808,55 @@ void Description::Media::parseSdpLine(string_view line) {
 	}
 	}
 }
 }
 
 
-Description::Media::RTPMap::RTPMap(string_view mline) {
+void Description::Media::addRTPMap(const Description::Media::RTPMap &map) {
+	mRtpMap.emplace(map.pt, map);
+}
+
+std::vector<uint32_t> Description::Media::getSSRCs() {
+	std::vector<uint32_t> vec;
+	for (auto &val : mAttributes) {
+		PLOG_DEBUG << val;
+		if (val.find("ssrc:") == 0) {
+			vec.emplace_back(std::stoul((std::string)val.substr(5, val.find(" "))));
+		}
+	}
+	return vec;
+}
+
+std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
+	return mRtpMap.begin();
+}
+
+std::map<int, Description::Media::RTPMap>::iterator Description::Media::endMaps() {
+	return mRtpMap.end();
+}
+
+std::map<int, Description::Media::RTPMap>::iterator
+Description::Media::removeMap(std::map<int, Description::Media::RTPMap>::iterator iterator) {
+	return mRtpMap.erase(iterator);
+}
+
+Description::Media::RTPMap::RTPMap(string_view mline) { setMLine(mline); }
+
+void Description::Media::RTPMap::removeFB(const string &str) {
+	auto it = rtcpFbs.begin();
+	while (it != rtcpFbs.end()) {
+		if (it->find(str) != std::string::npos) {
+			it = rtcpFbs.erase(it);
+		} else
+			it++;
+	}
+}
+
+void Description::Media::RTPMap::addFB(const string &str) { rtcpFbs.emplace_back(str); }
+
+int Description::Media::RTPMap::parsePT(string_view view) {
+	size_t p = view.find(' ');
+
+	return to_integer<int>(view.substr(0, p));
+}
+
+void Description::Media::RTPMap::setMLine(string_view mline) {
 	size_t p = mline.find(' ');
 	size_t p = mline.find(' ');
 
 
 	this->pt = to_integer<int>(mline.substr(0, p));
 	this->pt = to_integer<int>(mline.substr(0, p));
@@ -752,25 +874,15 @@ Description::Media::RTPMap::RTPMap(string_view mline) {
 		this->clockRate = to_integer<int>(line);
 		this->clockRate = to_integer<int>(line);
 	else {
 	else {
 		this->clockRate = to_integer<int>(line.substr(0, spl));
 		this->clockRate = to_integer<int>(line.substr(0, spl));
-		this->encParams = line.substr(spl);
-	}
-}
-
-void Description::Media::RTPMap::removeFB(const string &str) {
-	auto it = rtcpFbs.begin();
-	while (it != rtcpFbs.end()) {
-		if (it->find(str) != std::string::npos) {
-			it = rtcpFbs.erase(it);
-		} else
-			it++;
+		this->encParams = line.substr(spl + 1);
 	}
 	}
 }
 }
 
 
-void Description::Media::RTPMap::addFB(const string &str) { rtcpFbs.emplace_back(str); }
-
 Description::Audio::Audio(string mid, Direction dir)
 Description::Audio::Audio(string mid, Direction dir)
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
 
+void Description::Audio::addOpusCodec(int payloadType) { addAudioCodec(payloadType, "OPUS"); }
+
 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) {}
 
 
@@ -778,7 +890,7 @@ Description::Type Description::stringToType(const string &typeString) {
 	using TypeMap_t = std::unordered_map<string, Type>;
 	using TypeMap_t = std::unordered_map<string, Type>;
 	static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
 	static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
 	                                  {"offer", Type::Offer},
 	                                  {"offer", Type::Offer},
-	                                  {"answer", Type::Pranswer},
+	                                  {"answer", Type::Answer},
 	                                  {"pranswer", Type::Pranswer},
 	                                  {"pranswer", Type::Pranswer},
 	                                  {"rollback", Type::Rollback}};
 	                                  {"rollback", Type::Rollback}};
 	auto it = TypeMap.find(typeString);
 	auto it = TypeMap.find(typeString);

+ 80 - 26
src/dtlssrtptransport.cpp

@@ -82,7 +82,7 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
 		return false;
 		return false;
 	}
 	}
 
 
-	int size = message->size();
+	int size = int(message->size());
 	PLOG_VERBOSE << "Send size=" << size;
 	PLOG_VERBOSE << "Send size=" << size;
 
 
 	// The RTP header has a minimum size of 12 bytes
 	// The RTP header has a minimum size of 12 bytes
@@ -109,16 +109,31 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
 		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)) {
 			if (err == srtp_err_status_replay_fail)
 			if (err == srtp_err_status_replay_fail)
 				throw std::runtime_error("SRTCP packet is a replay");
 				throw std::runtime_error("SRTCP packet is a replay");
-			else
+			else if (err == srtp_err_status_no_ctx) {
+				auto ssrc = ((RTCP_SR *)message->data())->senderSSRC();
+				PLOG_INFO << "Adding SSRC to SRTCP: " << ssrc;
+				addSSRC(ssrc);
+				if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
+					throw std::runtime_error("SRTCP protect error, status=" +
+					                         to_string(static_cast<int>(err)));
+			} else {
 				throw std::runtime_error("SRTCP protect error, status=" +
 				throw std::runtime_error("SRTCP protect error, status=" +
 				                         to_string(static_cast<int>(err)));
 				                         to_string(static_cast<int>(err)));
+			}
 		}
 		}
 		PLOG_VERBOSE << "Protected SRTCP packet, size=" << size;
 		PLOG_VERBOSE << "Protected SRTCP packet, size=" << size;
 	} else {
 	} else {
 		if (srtp_err_status_t err = srtp_protect(mSrtpOut, message->data(), &size)) {
 		if (srtp_err_status_t err = srtp_protect(mSrtpOut, message->data(), &size)) {
 			if (err == srtp_err_status_replay_fail)
 			if (err == srtp_err_status_replay_fail)
-				throw std::runtime_error("SRTP packet is a replay");
-			else
+				throw std::runtime_error("Outgoing SRTP packet is a replay");
+			else if (err == srtp_err_status_no_ctx) {
+				auto ssrc = ((RTP *)message->data())->ssrc();
+				PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
+				addSSRC(ssrc);
+				if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
+					throw std::runtime_error("SRTCP protect error, status=" +
+					                         to_string(static_cast<int>(err)));
+			} else
 				throw std::runtime_error("SRTP protect error, status=" +
 				throw std::runtime_error("SRTP protect error, status=" +
 				                         to_string(static_cast<int>(err)));
 				                         to_string(static_cast<int>(err)));
 		}
 		}
@@ -127,7 +142,6 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
 
 
 	message->resize(size);
 	message->resize(size);
 	return outgoing(message);
 	return outgoing(message);
-//	return DtlsTransport::send(message);
 }
 }
 
 
 void DtlsSrtpTransport::incoming(message_ptr message) {
 void DtlsSrtpTransport::incoming(message_ptr message) {
@@ -137,7 +151,7 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
 		return;
 		return;
 	}
 	}
 
 
-	int size = message->size();
+	int size = int(message->size());
 	if (size == 0)
 	if (size == 0)
 		return;
 		return;
 
 
@@ -174,13 +188,23 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
 					PLOG_WARNING << "Incoming SRTCP packet is a replay";
 					PLOG_WARNING << "Incoming SRTCP packet is a replay";
 				else if (err == srtp_err_status_auth_fail)
 				else if (err == srtp_err_status_auth_fail)
 					PLOG_WARNING << "Incoming SRTCP packet failed authentication check";
 					PLOG_WARNING << "Incoming SRTCP packet failed authentication check";
-				else
-					PLOG_WARNING << "SRTCP unprotect error, status=" << err;
+				else if (err == srtp_err_status_no_ctx) {
+					auto ssrc = ((RTCP_SR *)message->data())->senderSSRC();
+					PLOG_INFO << "Adding SSRC to RTCP: " << ssrc;
+					addSSRC(ssrc);
+					if ((err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)))
+						throw std::runtime_error("SRTCP unprotect error, status=" +
+						                         to_string(static_cast<int>(err)));
+				} else {
+					PLOG_WARNING << "SRTCP unprotect error, status=" << err
+					             << " SSRC=" << ((RTCP_SR *)message->data())->senderSSRC();
+				}
 				return;
 				return;
 			}
 			}
 			PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
 			PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
 			message->type = Message::Type::Control;
 			message->type = Message::Type::Control;
-			message->stream = to_integer<uint8_t>(*(message->begin() + 1)); // Payload Type
+			auto rtp = (RTCP_SR *)message->data();
+			message->stream = rtp->senderSSRC();
 		} else {
 		} else {
 			PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
 			PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
 			if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
 			if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
@@ -188,13 +212,22 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
 					PLOG_WARNING << "Incoming SRTP packet is a replay";
 					PLOG_WARNING << "Incoming SRTP packet is a replay";
 				else if (err == srtp_err_status_auth_fail)
 				else if (err == srtp_err_status_auth_fail)
 					PLOG_WARNING << "Incoming SRTP packet failed authentication check";
 					PLOG_WARNING << "Incoming SRTP packet failed authentication check";
-				else
-					PLOG_WARNING << "SRTP unprotect error, status=" << err;
+				else if (err == srtp_err_status_no_ctx) {
+					auto ssrc = ((RTP *)message->data())->ssrc();
+					PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
+					addSSRC(ssrc);
+					if ((err = srtp_unprotect(mSrtpIn, message->data(), &size)))
+						throw std::runtime_error("SRTCP unprotect error, status=" +
+						                         to_string(static_cast<int>(err)));
+				} else
+					PLOG_WARNING << "SRTP unprotect error, status=" << err
+					             << " SSRC=" << ((RTP *)message->data())->ssrc();
 				return;
 				return;
 			}
 			}
 			PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
 			PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
 			message->type = Message::Type::Binary;
 			message->type = Message::Type::Binary;
-			message->stream = value2; // Payload Type
+			auto rtp = (RTP *)message->data();
+			message->stream = rtp->ssrc();
 		}
 		}
 
 
 		message->resize(size);
 		message->resize(size);
@@ -209,6 +242,8 @@ void DtlsSrtpTransport::postHandshake() {
 	if (mInitDone)
 	if (mInitDone)
 		return;
 		return;
 
 
+	static_assert(SRTP_AES_ICM_128_KEY_LEN_WSALT == SRTP_AES_128_KEY_LEN + SRTP_SALT_LEN);
+
 	const size_t materialLen = SRTP_AES_ICM_128_KEY_LEN_WSALT * 2;
 	const size_t materialLen = SRTP_AES_ICM_128_KEY_LEN_WSALT * 2;
 	unsigned char material[materialLen];
 	unsigned char material[materialLen];
 	const unsigned char *clientKey, *clientSalt, *serverKey, *serverSalt;
 	const unsigned char *clientKey, *clientSalt, *serverKey, *serverSalt;
@@ -257,22 +292,42 @@ void DtlsSrtpTransport::postHandshake() {
 	serverSalt = clientSalt + SRTP_SALT_LEN;
 	serverSalt = clientSalt + SRTP_SALT_LEN;
 #endif
 #endif
 
 
-	unsigned char clientSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
-	std::memcpy(clientSessionKey, clientKey, SRTP_AES_128_KEY_LEN);
-	std::memcpy(clientSessionKey + SRTP_AES_128_KEY_LEN, clientSalt, SRTP_SALT_LEN);
+	std::memcpy(mClientSessionKey, clientKey, SRTP_AES_128_KEY_LEN);
+	std::memcpy(mClientSessionKey + SRTP_AES_128_KEY_LEN, clientSalt, SRTP_SALT_LEN);
 
 
-	unsigned char serverSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
-	std::memcpy(serverSessionKey, serverKey, SRTP_AES_128_KEY_LEN);
-	std::memcpy(serverSessionKey + SRTP_AES_128_KEY_LEN, serverSalt, SRTP_SALT_LEN);
+	std::memcpy(mServerSessionKey, serverKey, SRTP_AES_128_KEY_LEN);
+	std::memcpy(mServerSessionKey + SRTP_AES_128_KEY_LEN, serverSalt, SRTP_SALT_LEN);
 
 
+	// Add SSRC=1 as an inbound because that is what Chrome does.
 	srtp_policy_t inbound = {};
 	srtp_policy_t inbound = {};
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
-	inbound.ssrc.type = ssrc_any_inbound;
-	inbound.ssrc.value = 0;
-	inbound.key = mIsClient ? serverSessionKey : clientSessionKey;
+	inbound.ssrc.type = ssrc_specific;
+	inbound.ssrc.value = 1;
+	inbound.key = mIsClient ? mServerSessionKey : mClientSessionKey;
 	inbound.next = nullptr;
 	inbound.next = nullptr;
 
 
+	if (srtp_err_status_t err = srtp_add_stream(mSrtpIn, &inbound)) {
+		throw std::runtime_error("SRTP add inbound stream failed, status=" +
+		                         to_string(static_cast<int>(err)));
+	}
+
+	mInitDone = true;
+}
+
+void DtlsSrtpTransport::addSSRC(uint32_t ssrc) {
+	if (!mInitDone)
+		throw std::logic_error("Attempted to add SSRC before SRTP keying material is derived");
+
+	srtp_policy_t inbound = {};
+	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
+	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
+	inbound.ssrc.type = ssrc_specific;
+	inbound.ssrc.value = ssrc;
+	inbound.key = mIsClient ? mServerSessionKey : mClientSessionKey;
+	inbound.next = nullptr;
+	inbound.allow_repeat_tx = true;
+
 	if (srtp_err_status_t err = srtp_add_stream(mSrtpIn, &inbound))
 	if (srtp_err_status_t err = srtp_add_stream(mSrtpIn, &inbound))
 		throw std::runtime_error("SRTP add inbound stream failed, status=" +
 		throw std::runtime_error("SRTP add inbound stream failed, status=" +
 		                         to_string(static_cast<int>(err)));
 		                         to_string(static_cast<int>(err)));
@@ -280,16 +335,15 @@ void DtlsSrtpTransport::postHandshake() {
 	srtp_policy_t outbound = {};
 	srtp_policy_t outbound = {};
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
 	srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
-	outbound.ssrc.type = ssrc_any_outbound;
-	outbound.ssrc.value = 0;
-	outbound.key = mIsClient ? clientSessionKey : serverSessionKey;
+	outbound.ssrc.type = ssrc_specific;
+	outbound.ssrc.value = ssrc;
+	outbound.key = mIsClient ? mClientSessionKey : mServerSessionKey;
 	outbound.next = nullptr;
 	outbound.next = nullptr;
+	outbound.allow_repeat_tx = true;
 
 
 	if (srtp_err_status_t err = srtp_add_stream(mSrtpOut, &outbound))
 	if (srtp_err_status_t err = srtp_add_stream(mSrtpOut, &outbound))
 		throw std::runtime_error("SRTP add outbound stream failed, status=" +
 		throw std::runtime_error("SRTP add outbound stream failed, status=" +
 		                         to_string(static_cast<int>(err)));
 		                         to_string(static_cast<int>(err)));
-
-	mInitDone = true;
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc

+ 11 - 1
src/dtlssrtptransport.hpp

@@ -24,7 +24,13 @@
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 
 
+#if RTC_SYSTEM_SRTP
 #include <srtp2/srtp.h>
 #include <srtp2/srtp.h>
+#else
+#include "srtp.h"
+#endif
+
+#include <atomic>
 
 
 namespace rtc {
 namespace rtc {
 
 
@@ -39,6 +45,7 @@ public:
 	~DtlsSrtpTransport();
 	~DtlsSrtpTransport();
 
 
 	bool sendMedia(message_ptr message);
 	bool sendMedia(message_ptr message);
+	void addSSRC(uint32_t ssrc);
 
 
 private:
 private:
 	void incoming(message_ptr message) override;
 	void incoming(message_ptr message) override;
@@ -47,7 +54,10 @@ private:
 	message_callback mSrtpRecvCallback;
 	message_callback mSrtpRecvCallback;
 
 
 	srtp_t mSrtpIn, mSrtpOut;
 	srtp_t mSrtpIn, mSrtpOut;
-	bool mInitDone = false;
+
+	std::atomic<bool> mInitDone = false;
+	unsigned char mClientSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
+	unsigned char mServerSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 2 - 3
src/dtlstransport.cpp

@@ -177,8 +177,8 @@ void DtlsTransport::runRecvLoop() {
 	// Receive loop
 	// Receive loop
 	try {
 	try {
 		PLOG_INFO << "DTLS handshake finished";
 		PLOG_INFO << "DTLS handshake finished";
-		changeState(State::Connected);
 		postHandshake();
 		postHandshake();
+		changeState(State::Connected);
 
 
 		const size_t bufferSize = maxMtu;
 		const size_t bufferSize = maxMtu;
 		char buffer[bufferSize];
 		char buffer[bufferSize];
@@ -453,8 +453,8 @@ void DtlsTransport::runRecvLoop() {
 						SSL_set_mtu(mSsl, maxMtu + 1);
 						SSL_set_mtu(mSsl, maxMtu + 1);
 
 
 						PLOG_INFO << "DTLS handshake finished";
 						PLOG_INFO << "DTLS handshake finished";
-						changeState(State::Connected);
 						postHandshake();
 						postHandshake();
+						changeState(State::Connected);
 					}
 					}
 				} else {
 				} else {
 					ret = SSL_read(mSsl, buffer, bufferSize);
 					ret = SSL_read(mSsl, buffer, bufferSize);
@@ -575,4 +575,3 @@ long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /
 #endif
 #endif
 
 
 } // namespace rtc
 } // namespace rtc
-

+ 0 - 1
src/dtlstransport.hpp

@@ -92,4 +92,3 @@ protected:
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif
-

+ 10 - 8
src/icetransport.cpp

@@ -130,9 +130,7 @@ IceTransport::~IceTransport() {
 	mAgent.reset();
 	mAgent.reset();
 }
 }
 
 
-bool IceTransport::stop() {
-	return Transport::stop();
-}
+bool IceTransport::stop() { return Transport::stop(); }
 
 
 Description::Role IceTransport::role() const { return mRole; }
 Description::Role IceTransport::role() const { return mRole; }
 
 
@@ -141,12 +139,14 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
 	if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
 	if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
 		throw std::runtime_error("Failed to generate local SDP");
 		throw std::runtime_error("Failed to generate local SDP");
 
 
-	return Description(string(sdp), type, mRole);
+	return Description(string(sdp), type,
+	                   type == Description::Type::Offer ? Description::Role::ActPass : mRole);
 }
 }
 
 
 void IceTransport::setRemoteDescription(const Description &description) {
 void IceTransport::setRemoteDescription(const Description &description) {
 	mRole = description.role() == Description::Role::Active ? Description::Role::Passive
 	mRole = description.role() == Description::Role::Active ? Description::Role::Passive
 	                                                        : Description::Role::Active;
 	                                                        : Description::Role::Active;
+
 	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)
@@ -191,7 +191,7 @@ bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote)
 	char sdpLocal[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
 	char sdpLocal[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
 	char sdpRemote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
 	char sdpRemote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
 	if (juice_get_selected_candidates(mAgent.get(), sdpLocal, JUICE_MAX_CANDIDATE_SDP_STRING_LEN,
 	if (juice_get_selected_candidates(mAgent.get(), sdpLocal, JUICE_MAX_CANDIDATE_SDP_STRING_LEN,
-	                                 sdpRemote, JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0) {
+	                                  sdpRemote, JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0) {
 		if (local) {
 		if (local) {
 			*local = Candidate(sdpLocal, mMid);
 			*local = Candidate(sdpLocal, mMid);
 			local->resolve(Candidate::ResolveMode::Simple);
 			local->resolve(Candidate::ResolveMode::Simple);
@@ -736,15 +736,17 @@ void IceTransport::LogCallback(const gchar * /*logDomain*/, GLogLevelFlags logLe
 
 
 bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote) {
 bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote) {
 	NiceCandidate *niceLocal, *niceRemote;
 	NiceCandidate *niceLocal, *niceRemote;
-	if(!nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &niceLocal, &niceRemote))
+	if (!nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &niceLocal, &niceRemote))
 		return false;
 		return false;
 
 
 	gchar *sdpLocal = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceLocal);
 	gchar *sdpLocal = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceLocal);
-	if(local) *local = Candidate(sdpLocal, mMid);
+	if (local)
+		*local = Candidate(sdpLocal, mMid);
 	g_free(sdpLocal);
 	g_free(sdpLocal);
 
 
 	gchar *sdpRemote = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceRemote);
 	gchar *sdpRemote = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceRemote);
-	if(remote) *remote = Candidate(sdpRemote, mMid);
+	if (remote)
+		*remote = Candidate(sdpRemote, mMid);
 	g_free(sdpRemote);
 	g_free(sdpRemote);
 
 
 	if (local)
 	if (local)

+ 1 - 1
src/icetransport.hpp

@@ -20,8 +20,8 @@
 #define RTC_ICE_TRANSPORT_H
 #define RTC_ICE_TRANSPORT_H
 
 
 #include "candidate.hpp"
 #include "candidate.hpp"
-#include "description.hpp"
 #include "configuration.hpp"
 #include "configuration.hpp"
+#include "description.hpp"
 #include "include.hpp"
 #include "include.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
 #include "transport.hpp"
 #include "transport.hpp"

+ 0 - 1
src/init.cpp

@@ -142,4 +142,3 @@ Init::~Init() {
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc
-

+ 1 - 2
src/log.cpp

@@ -44,5 +44,4 @@ void InitLogger(plog::Severity severity, plog::IAppender *appender) {
 			logger->addAppender(appender);
 			logger->addAppender(appender);
 	}
 	}
 }
 }
-}
-
+} // namespace rtc

+ 157 - 68
src/peerconnection.cpp

@@ -1,5 +1,6 @@
 /**
 /**
  * Copyright (c) 2019 Paul-Louis Ageneau
  * Copyright (c) 2019 Paul-Louis Ageneau
+ * Copyright (c) 2020 Filip Klembara (in2core)
  *
  *
  * This library is free software; you can redistribute it and/or
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * modify it under the terms of the GNU Lesser General Public
@@ -31,8 +32,20 @@
 #endif
 #endif
 
 
 #include <iomanip>
 #include <iomanip>
+#include <set>
 #include <thread>
 #include <thread>
 
 
+#if __clang__
+namespace {
+template <typename To, typename From>
+inline std::shared_ptr<To> reinterpret_pointer_cast(std::shared_ptr<From> const &ptr) noexcept {
+	return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get()));
+}
+} // namespace
+#else
+using std::reinterpret_pointer_cast;
+#endif
+
 namespace rtc {
 namespace rtc {
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
@@ -311,8 +324,7 @@ std::optional<string> PeerConnection::remoteAddress() const {
 	return iceTransport ? iceTransport->getRemoteAddress() : nullopt;
 	return iceTransport ? iceTransport->getRemoteAddress() : nullopt;
 }
 }
 
 
-shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string protocol,
-                                                       Reliability reliability) {
+shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, DataChannelInit init) {
 	// RFC 5763: The answerer MUST use either a setup attribute value of setup:active or
 	// RFC 5763: The answerer MUST use either a setup attribute value of setup:active or
 	// setup:passive. [...] Thus, setup:active is RECOMMENDED.
 	// setup:passive. [...] Thus, setup:active is RECOMMENDED.
 	// See https://tools.ietf.org/html/rfc5763#section-5
 	// See https://tools.ietf.org/html/rfc5763#section-5
@@ -320,8 +332,7 @@ shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string prot
 	auto iceTransport = std::atomic_load(&mIceTransport);
 	auto iceTransport = std::atomic_load(&mIceTransport);
 	auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
 	auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
 
 
-	auto channel =
-	    emplaceDataChannel(role, std::move(label), std::move(protocol), std::move(reliability));
+	auto channel = emplaceDataChannel(role, std::move(label), std::move(init));
 
 
 	if (auto transport = std::atomic_load(&mSctpTransport))
 	if (auto transport = std::atomic_load(&mSctpTransport))
 		if (transport->state() == SctpTransport::State::Connected)
 		if (transport->state() == SctpTransport::State::Connected)
@@ -335,9 +346,8 @@ shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string prot
 	return channel;
 	return channel;
 }
 }
 
 
-shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, string protocol,
-                                                          Reliability reliability) {
-	auto channel = addDataChannel(label, protocol, reliability);
+shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, DataChannelInit init) {
+	auto channel = addDataChannel(std::move(label), std::move(init));
 	setLocalDescription();
 	setLocalDescription();
 	return channel;
 	return channel;
 }
 }
@@ -382,6 +392,7 @@ std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description)
 	if (!track) {
 	if (!track) {
 		track = std::make_shared<Track>(std::move(description));
 		track = std::make_shared<Track>(std::move(description));
 		mTracks.emplace(std::make_pair(track->mid(), track));
 		mTracks.emplace(std::make_pair(track->mid(), track));
+		mTrackLines.emplace_back(track);
 	}
 	}
 
 
 	// Renegotiation is needed for the new or updated track
 	// Renegotiation is needed for the new or updated track
@@ -634,7 +645,8 @@ void PeerConnection::forwardMessage(message_ptr message) {
 		return;
 		return;
 	}
 	}
 
 
-	auto channel = findDataChannel(uint16_t(message->stream));
+	uint16_t stream = uint16_t(message->stream);
+	auto channel = findDataChannel(stream);
 	if (!channel) {
 	if (!channel) {
 		auto iceTransport = std::atomic_load(&mIceTransport);
 		auto iceTransport = std::atomic_load(&mIceTransport);
 		auto sctpTransport = std::atomic_load(&mSctpTransport);
 		auto sctpTransport = std::atomic_load(&mSctpTransport);
@@ -642,15 +654,15 @@ void PeerConnection::forwardMessage(message_ptr message) {
 			return;
 			return;
 
 
 		const byte dataChannelOpenMessage{0x03};
 		const byte dataChannelOpenMessage{0x03};
-		unsigned int remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
+		uint16_t remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
 		if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
 		if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
-		    message->stream % 2 == remoteParity) {
+		    stream % 2 == remoteParity) {
 
 
-			channel =
-			    std::make_shared<DataChannel>(shared_from_this(), sctpTransport, message->stream);
+			channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport,
+			                                                  message->stream);
 			channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
 			channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
 			                          weak_ptr<DataChannel>{channel}));
 			                          weak_ptr<DataChannel>{channel}));
-			mDataChannels.insert(std::make_pair(message->stream, channel));
+			mDataChannels.emplace(message->stream, channel);
 		} else {
 		} else {
 			// Invalid, close the DataChannel
 			// Invalid, close the DataChannel
 			sctpTransport->closeStream(message->stream);
 			sctpTransport->closeStream(message->stream);
@@ -665,53 +677,117 @@ void PeerConnection::forwardMedia(message_ptr message) {
 	if (!message)
 	if (!message)
 		return;
 		return;
 
 
-	if (message->type == Message::Type::Control) {
+	// Browsers like to compound their packets with a random SSRC.
+	// we have to do this monstrosity to distribute the report blocks
+	if (message->type == Message::Control) {
+		std::set<uint32_t> ssrcs;
+		size_t offset = 0;
+		while ((sizeof(rtc::RTCP_HEADER) + offset) <= message->size()) {
+			auto header = reinterpret_cast<rtc::RTCP_HEADER *>(message->data() + offset);
+			if (header->lengthInBytes() > message->size() - offset) {
+				PLOG_WARNING << "RTCP packet is truncated";
+				break;
+			}
+			offset += header->lengthInBytes();
+			if (header->payloadType() == 205 || header->payloadType() == 206) {
+				auto rtcpfb = reinterpret_cast<RTCP_FB_HEADER *>(header);
+				ssrcs.insert(rtcpfb->getPacketSenderSSRC());
+				ssrcs.insert(rtcpfb->getMediaSourceSSRC());
+
+			} else if (header->payloadType() == 200 || header->payloadType() == 201) {
+				auto rtcpsr = reinterpret_cast<RTCP_SR *>(header);
+				ssrcs.insert(rtcpsr->senderSSRC());
+				for (int i = 0; i < rtcpsr->header.reportCount(); ++i)
+					ssrcs.insert(rtcpsr->getReportBlock(i)->getSSRC());
+			} else {
+				// PT=202 == SDES
+				// PT=207 == Extended Report
+				if (header->payloadType() != 202 && header->payloadType() != 207) {
+					PLOG_WARNING << "Unknown packet type: " << (int)header->version() << " "
+					             << header->payloadType() << "";
+				}
+			}
+		}
+
+		if (!ssrcs.empty()) {
+			for (uint32_t ssrc : ssrcs) {
+				if (auto mid = getMidFromSsrc(ssrc)) {
+					std::shared_lock lock(mTracksMutex); // read-only
+					if (auto it = mTracks.find(*mid); it != mTracks.end())
+						if (auto track = it->second.lock())
+							track->incoming(message);
+				}
+			}
+			return;
+		}
+	}
+
+	uint32_t ssrc = uint32_t(message->stream);
+	if (auto mid = getMidFromSsrc(ssrc)) {
 		std::shared_lock lock(mTracksMutex); // read-only
 		std::shared_lock lock(mTracksMutex); // read-only
-		for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
+		if (auto it = mTracks.find(*mid); it != mTracks.end())
 			if (auto track = it->second.lock())
 			if (auto track = it->second.lock())
-				return track->incoming(message);
-
-		PLOG_WARNING << "No track available to receive control, dropping";
+				track->incoming(message);
+	} else {
+		/*
+		 * TODO: So the problem is that when stop sending streams, we stop getting report blocks for
+		 * those streams Therefore when we get compound RTCP packets, they are empty, and we can't
+		 * forward them. Therefore, it is expected that we don't know where to forward packets. Is
+		 * this ideal? No! Do I know how to fix it? No!
+		 */
+		// PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
 		return;
 		return;
 	}
 	}
+} // namespace rtc
 
 
-	unsigned int payloadType = message->stream;
-	std::optional<string> mid;
-	if (auto it = mMidFromPayloadType.find(payloadType); it != mMidFromPayloadType.end()) {
-		mid = it->second;
-	} else {
-		std::lock_guard lock(mLocalDescriptionMutex);
-		if (!mLocalDescription)
-			return;
+std::optional<std::string> PeerConnection::getMidFromSsrc(uint32_t ssrc) {
+	if (auto it = mMidFromSsrc.find(ssrc); it != mMidFromSsrc.end())
+		return it->second;
 
 
-		for (int i = 0; i < mLocalDescription->mediaCount(); ++i) {
+	{
+		std::lock_guard lock(mRemoteDescriptionMutex);
+		if (!mRemoteDescription)
+			return nullopt;
+		for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
 			if (auto found = std::visit(
 			if (auto found = std::visit(
 			        rtc::overloaded{[&](Description::Application *) -> std::optional<string> {
 			        rtc::overloaded{[&](Description::Application *) -> std::optional<string> {
 				                        return std::nullopt;
 				                        return std::nullopt;
 			                        },
 			                        },
 			                        [&](Description::Media *media) -> std::optional<string> {
 			                        [&](Description::Media *media) -> std::optional<string> {
-				                        return media->hasPayloadType(payloadType)
+				                        return media->hasSSRC(ssrc)
 				                                   ? std::make_optional(media->mid())
 				                                   ? std::make_optional(media->mid())
 				                                   : nullopt;
 				                                   : nullopt;
 			                        }},
 			                        }},
-			        mLocalDescription->media(i))) {
+			        mRemoteDescription->media(i))) {
 
 
-				mMidFromPayloadType.emplace(payloadType, *found);
-				mid = *found;
-				break;
+				mMidFromSsrc.emplace(ssrc, *found);
+				return *found;
 			}
 			}
 		}
 		}
 	}
 	}
+	{
+		std::lock_guard lock(mLocalDescriptionMutex);
+		if (!mLocalDescription)
+			return nullopt;
+		for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
+			if (auto found = std::visit(
+			        rtc::overloaded{[&](Description::Application *) -> std::optional<string> {
+				                        return std::nullopt;
+			                        },
+			                        [&](Description::Media *media) -> std::optional<string> {
+				                        return media->hasSSRC(ssrc)
+				                                   ? std::make_optional(media->mid())
+				                                   : nullopt;
+			                        }},
+			        mLocalDescription->media(i))) {
 
 
-	if (!mid) {
-		PLOG_WARNING << "Track not found for payload type " << payloadType << ", dropping";
-		return;
+				mMidFromSsrc.emplace(ssrc, *found);
+				return *found;
+			}
+		}
 	}
 	}
 
 
-	std::shared_lock lock(mTracksMutex); // read-only
-	if (auto it = mTracks.find(*mid); it != mTracks.end())
-		if (auto track = it->second.lock())
-			track->incoming(message);
+	return nullopt;
 }
 }
 
 
 void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
 void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
@@ -720,20 +796,33 @@ void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
 }
 }
 
 
 shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role, string label,
 shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role, string label,
-                                                           string protocol,
-                                                           Reliability reliability) {
-	// The active side must use streams with even identifiers, whereas the passive side must use
-	// streams with odd identifiers.
-	// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
+                                                           DataChannelInit init) {
 	std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
 	std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
-	unsigned int stream = (role == Description::Role::Active) ? 0 : 1;
-	while (mDataChannels.find(stream) != mDataChannels.end()) {
-		stream += 2;
-		if (stream >= 65535)
-			throw std::runtime_error("Too many DataChannels");
+	uint16_t stream;
+	if (init.id) {
+		stream = *init.id;
+		if (stream == 65535)
+			throw std::invalid_argument("Invalid DataChannel id");
+	} else {
+		// The active side must use streams with even identifiers, whereas the passive side must use
+		// streams with odd identifiers.
+		// See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
+		stream = (role == Description::Role::Active) ? 0 : 1;
+		while (mDataChannels.find(stream) != mDataChannels.end()) {
+			if (stream >= 65535 - 2)
+				throw std::runtime_error("Too many DataChannels");
+
+			stream += 2;
+		}
 	}
 	}
-	auto channel = std::make_shared<DataChannel>(shared_from_this(), stream, std::move(label),
-	                                             std::move(protocol), std::move(reliability));
+	// If the DataChannel is user-negotiated, do not negociate it here
+	auto channel =
+	    init.negotiated
+	        ? std::make_shared<DataChannel>(shared_from_this(), stream, std::move(label),
+	                                        std::move(init.protocol), std::move(init.reliability))
+	        : std::make_shared<NegociatedDataChannel>(shared_from_this(), stream, std::move(label),
+	                                                  std::move(init.protocol),
+	                                                  std::move(init.reliability));
 	mDataChannels.emplace(std::make_pair(stream, channel));
 	mDataChannels.emplace(std::make_pair(stream, channel));
 	return channel;
 	return channel;
 }
 }
@@ -800,14 +889,15 @@ void PeerConnection::incomingTrack(Description::Media description) {
 	if (mTracks.find(description.mid()) == mTracks.end()) {
 	if (mTracks.find(description.mid()) == mTracks.end()) {
 		auto track = std::make_shared<Track>(std::move(description));
 		auto track = std::make_shared<Track>(std::move(description));
 		mTracks.emplace(std::make_pair(track->mid(), track));
 		mTracks.emplace(std::make_pair(track->mid(), track));
-		triggerTrack(std::move(track));
+		mTrackLines.emplace_back(track);
+		triggerTrack(track);
 	}
 	}
 }
 }
 
 
 void PeerConnection::openTracks() {
 void PeerConnection::openTracks() {
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 	if (auto transport = std::atomic_load(&mDtlsTransport)) {
 	if (auto transport = std::atomic_load(&mDtlsTransport)) {
-		auto srtpTransport = std::reinterpret_pointer_cast<DtlsSrtpTransport>(transport);
+		auto srtpTransport = reinterpret_pointer_cast<DtlsSrtpTransport>(transport);
 		std::shared_lock lock(mTracksMutex); // read-only
 		std::shared_lock lock(mTracksMutex); // read-only
 		for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
 		for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
 			if (auto track = it->second.lock())
 			if (auto track = it->second.lock())
@@ -831,7 +921,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
 		throw std::invalid_argument("Remote description has no media line");
 		throw std::invalid_argument("Remote description has no media line");
 
 
 	int activeMediaCount = 0;
 	int activeMediaCount = 0;
-	for (int i = 0; i < description.mediaCount(); ++i)
+	for (unsigned int i = 0; i < description.mediaCount(); ++i)
 		std::visit(rtc::overloaded{[&](const Description::Application *) { ++activeMediaCount; },
 		std::visit(rtc::overloaded{[&](const Description::Application *) { ++activeMediaCount; },
 		                           [&](const Description::Media *media) {
 		                           [&](const Description::Media *media) {
 			                           if (media->direction() != Description::Direction::Inactive)
 			                           if (media->direction() != Description::Direction::Inactive)
@@ -851,9 +941,10 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
 }
 }
 
 
 void PeerConnection::processLocalDescription(Description description) {
 void PeerConnection::processLocalDescription(Description description) {
+
 	if (auto remote = remoteDescription()) {
 	if (auto remote = remoteDescription()) {
 		// Reciprocate remote description
 		// Reciprocate remote description
-		for (int i = 0; i < remote->mediaCount(); ++i)
+		for (unsigned int i = 0; i < remote->mediaCount(); ++i)
 			std::visit( // reciprocate each media
 			std::visit( // reciprocate each media
 			    rtc::overloaded{
 			    rtc::overloaded{
 			        [&](Description::Application *remoteApp) {
 			        [&](Description::Application *remoteApp) {
@@ -907,6 +998,7 @@ void PeerConnection::processLocalDescription(Description description) {
 					        }
 					        }
 					        return;
 					        return;
 				        }
 				        }
+				        lock.unlock(); // we are going to call incomingTrack()
 
 
 				        auto reciprocated = remoteMedia->reciprocate();
 				        auto reciprocated = remoteMedia->reciprocate();
 #if !RTC_ENABLE_MEDIA
 #if !RTC_ENABLE_MEDIA
@@ -945,11 +1037,11 @@ void PeerConnection::processLocalDescription(Description description) {
 
 
 		// Add media for local tracks
 		// Add media for local tracks
 		std::shared_lock lock(mTracksMutex);
 		std::shared_lock lock(mTracksMutex);
-		for (auto it = mTracks.begin(); it != mTracks.end(); ++it) {
-			if (description.hasMid(it->first))
-				continue;
+		for (auto it = mTrackLines.begin(); it != mTrackLines.end(); ++it) {
+			if (auto track = it->lock()) {
+				if (description.hasMid(track->mid()))
+					continue;
 
 
-			if (auto track = it->second.lock()) {
 				auto media = track->description();
 				auto media = track->description();
 #if !RTC_ENABLE_MEDIA
 #if !RTC_ENABLE_MEDIA
 				// No media support, mark as inactive
 				// No media support, mark as inactive
@@ -1029,9 +1121,12 @@ void PeerConnection::processRemoteDescription(Description description) {
 }
 }
 
 
 void PeerConnection::processRemoteCandidate(Candidate candidate) {
 void PeerConnection::processRemoteCandidate(Candidate candidate) {
+	std::lock_guard lock(mRemoteDescriptionMutex);
 	auto iceTransport = std::atomic_load(&mIceTransport);
 	auto iceTransport = std::atomic_load(&mIceTransport);
-	if (!iceTransport)
-		throw std::logic_error("Remote candidate set without remote description");
+	if (!mRemoteDescription || !iceTransport)
+		throw std::logic_error("Got a remote candidate without remote description");
+
+	candidate.hintMid(mRemoteDescription->bundleMid());
 
 
 	if (candidate.resolve(Candidate::ResolveMode::Simple)) {
 	if (candidate.resolve(Candidate::ResolveMode::Simple)) {
 		iceTransport->addRemoteCandidate(candidate);
 		iceTransport->addRemoteCandidate(candidate);
@@ -1047,13 +1142,7 @@ void PeerConnection::processRemoteCandidate(Candidate candidate) {
 		t.detach();
 		t.detach();
 	}
 	}
 
 
-	{
-		std::lock_guard lock(mRemoteDescriptionMutex);
-		if (!mRemoteDescription)
-			throw std::logic_error("Got a remote candidate without remote description");
-
-		mRemoteDescription->addCandidate(candidate);
-	}
+	mRemoteDescription->addCandidate(std::move(candidate));
 }
 }
 
 
 void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
 void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {

+ 0 - 1
src/processor.cpp

@@ -41,4 +41,3 @@ void Processor::schedule() {
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc
-

+ 1 - 2
src/processor.hpp

@@ -44,8 +44,7 @@ public:
 
 
 	void join();
 	void join();
 
 
-	template <class F, class... Args>
-	void enqueue(F &&f, Args &&... args);
+	template <class F, class... Args> void enqueue(F &&f, Args &&... args);
 
 
 protected:
 protected:
 	void schedule();
 	void schedule();

+ 35 - 324
src/rtcp.cpp

@@ -1,5 +1,5 @@
 /**
 /**
- * Copyright (c) 2020 Staz M
+ * Copyright (c) 2020 Staz Modrzynski
  * Copyright (c) 2020 Paul-Louis Ageneau
  * Copyright (c) 2020 Paul-Louis Ageneau
  *
  *
  * This library is free software; you can redistribute it and/or
  * This library is free software; you can redistribute it and/or
@@ -19,6 +19,7 @@
 
 
 #include "rtcp.hpp"
 #include "rtcp.hpp"
 
 
+#include "track.hpp"
 #include <cmath>
 #include <cmath>
 #include <utility>
 #include <utility>
 
 
@@ -28,310 +29,11 @@
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #endif
 #endif
 
 
-#ifndef htonll
-#define htonll(x)                                                                                  \
-	((uint64_t)htonl(((uint64_t)(x)&0xFFFFFFFF) << 32) | (uint64_t)htonl((uint64_t)(x) >> 32))
-#endif
-#ifndef ntohll
-#define ntohll(x) htonll(x)
-#endif
-
 namespace rtc {
 namespace rtc {
 
 
-#pragma pack(push, 1)
-
-struct RTP {
-private:
-	uint8_t _first;
-	uint8_t _payloadType;
-	uint16_t _seqNumber;
-	uint32_t _timestamp;
-
-public:
-	SSRC ssrc;
-	SSRC csrc[16];
-
-	inline uint8_t version() const { return _first >> 6; }
-	inline bool padding() const { return (_first >> 5) & 0x01; }
-	inline uint8_t csrcCount() const { return _first & 0x0F; }
-	inline uint8_t payloadType() const { return _payloadType; }
-	inline uint16_t seqNumber() const { return ntohs(_seqNumber); }
-	inline uint32_t timestamp() const { return ntohl(_timestamp); }
-};
-
-struct RTCP_ReportBlock {
-	SSRC ssrc;
-
-private:
-	uint32_t _fractionLostAndPacketsLost; // fraction lost is 8-bit, packets lost is 24-bit
-	uint16_t _seqNoCycles;
-	uint16_t _highestSeqNo;
-	uint32_t _jitter;
-	uint32_t _lastReport;
-	uint32_t _delaySinceLastReport;
-
-public:
-	inline void preparePacket(SSRC ssrc_, [[maybe_unused]] unsigned int packetsLost,
-	                          [[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
-	                          uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
-	                          uint64_t lastSR_DELAY) {
-		setSeqNo(highestSeqNo, seqNoCycles);
-		setJitter(jitter);
-		setSSRC(ssrc_);
-
-		// Middle 32 bits of NTP Timestamp
-		// _lastReport = lastSR_NTP >> 16u;
-		setNTPOfSR(uint32_t(lastSR_NTP));
-		setDelaySinceSR(uint32_t(lastSR_DELAY));
-
-		// The delay, expressed in units of 1/65536 seconds
-		// _delaySinceLastReport = lastSR_DELAY;
-	}
-
-	inline void setSSRC(SSRC ssrc_) { ssrc = htonl(ssrc_); }
-	inline SSRC getSSRC() const { return ntohl(ssrc); }
-
-	inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
-	                           [[maybe_unused]] unsigned int totalPackets) {
-		// TODO Implement loss percentages.
-		_fractionLostAndPacketsLost = 0;
-	}
-	inline unsigned int getLossPercentage() const {
-		// TODO Implement loss percentages.
-		return 0;
-	}
-	inline unsigned int getPacketLostCount() const {
-		// TODO Implement total packets lost.
-		return 0;
-	}
-
-	inline uint16_t seqNoCycles() const { return ntohs(_seqNoCycles); }
-	inline uint16_t highestSeqNo() const { return ntohs(_highestSeqNo); }
-	inline uint32_t jitter() const { return ntohl(_jitter); }
-
-	inline void setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
-		_highestSeqNo = htons(highestSeqNo);
-		_seqNoCycles = htons(seqNoCycles);
-	}
-
-	inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
-
-	inline void setNTPOfSR(uint32_t ntp) { _lastReport = htonl(ntp >> 16u); }
-	inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
-
-	inline void setDelaySinceSR(uint32_t sr) {
-		// The delay, expressed in units of 1/65536 seconds
-		_delaySinceLastReport = htonl(sr);
-	}
-	inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
-
-	inline void log() const {
-		PLOG_DEBUG << "RTCP report block: "
-		           << "ssrc="
-		           << ntohl(ssrc)
-		           // TODO: Implement these reports
-		           //	<< ", fractionLost=" << fractionLost
-		           //	<< ", packetsLost=" << packetsLost
-		           << ", highestSeqNo=" << highestSeqNo() << ", seqNoCycles=" << seqNoCycles()
-		           << ", jitter=" << jitter() << ", lastSR=" << getNTPOfSR()
-		           << ", lastSRDelay=" << getDelaySinceSR();
-	}
-};
-
-struct RTCP_HEADER {
-private:
-	uint8_t _first;
-	uint8_t _payloadType;
-	uint16_t _length;
-
-public:
-	inline uint8_t version() const { return _first >> 6; }
-	inline bool padding() const { return (_first >> 5) & 0x01; }
-	inline uint8_t reportCount() const { return _first & 0x0F; }
-	inline uint8_t payloadType() const { return _payloadType; }
-	inline uint16_t length() const { return ntohs(_length); }
-
-	inline void setPayloadType(uint8_t type) { _payloadType = type; }
-	inline void setReportCount(uint8_t count) { _first = (_first & 0xF0) | (count & 0x0F); }
-	inline void setLength(uint16_t length) { _length = htons(length); }
-
-	inline void prepareHeader(uint8_t payloadType, uint8_t reportCount, uint16_t length) {
-		_first = 0x02 << 6; // version 2, no padding
-		setReportCount(reportCount);
-		setPayloadType(payloadType);
-		setLength(length);
-	}
-
-	inline void log() const {
-		PLOG_DEBUG << "RTCP header: "
-		           << "version=" << unsigned(version()) << ", padding=" << padding()
-		           << ", reportCount=" << unsigned(reportCount())
-		           << ", payloadType=" << unsigned(payloadType()) << ", length=" << length();
-	}
-};
-
-struct RTCP_SR {
-	RTCP_HEADER header;
-	SSRC senderSsrc;
-
-private:
-	uint64_t _ntpTimestamp;
-	uint32_t _rtpTimestamp;
-	uint32_t _packetCount;
-	uint32_t _octetCount;
-
-	RTCP_ReportBlock _reportBlocks;
-
-public:
-	inline void preparePacket(SSRC senderSsrc_, uint8_t reportCount) {
-		unsigned int length =
-		    ((sizeof(header) + 24 + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
-		header.prepareHeader(200, reportCount, uint16_t(length));
-		senderSsrc = htonl(senderSsrc_);
-	}
-
-	inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
-	inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
-
-	[[nodiscard]] inline size_t getSize() const {
-		// "length" in packet is one less than the number of 32 bit words in the packet.
-		return sizeof(uint32_t) * (1 + size_t(header.length()));
-	}
-
-	inline uint32_t ntpTimestamp() const { return ntohll(_ntpTimestamp); }
-	inline uint32_t rtpTimestamp() const { return ntohl(_rtpTimestamp); }
-	inline uint32_t packetCount() const { return ntohl(_packetCount); }
-	inline uint32_t octetCount() const { return ntohl(_octetCount); }
-
-	inline void setNtpTimestamp(uint32_t ts) { _ntpTimestamp = htonll(ts); }
-	inline void setRtpTimestamp(uint32_t ts) { _rtpTimestamp = htonl(ts); }
-
-	inline void log() const {
-		header.log();
-		PLOG_DEBUG << "RTCP SR: "
-		           << " SSRC=" << ntohl(senderSsrc) << ", NTP_TS=" << ntpTimestamp()
-		           << ", RTP_TS=" << rtpTimestamp() << ", packetCount=" << packetCount()
-		           << ", octetCount=" << octetCount();
-
-		for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
-			getReportBlock(i)->log();
-		}
-	}
-};
-
-struct RTCP_RR {
-	RTCP_HEADER header;
-	SSRC senderSsrc;
-
-private:
-	RTCP_ReportBlock _reportBlocks;
-
-public:
-	inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
-	inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
-
-	inline SSRC getSenderSSRC() const { return ntohl(senderSsrc); }
-	inline void setSenderSSRC(SSRC ssrc) { senderSsrc = htonl(ssrc); }
-
-	[[nodiscard]] inline size_t getSize() const {
-		// "length" in packet is one less than the number of 32 bit words in the packet.
-		return sizeof(uint32_t) * (1 + size_t(header.length()));
-	}
-
-	inline void preparePacket(SSRC ssrc, uint8_t reportCount) {
-		// "length" in packet is one less than the number of 32 bit words in the packet.
-		size_t length = (sizeWithReportBlocks(reportCount) / 4) - 1;
-		header.prepareHeader(201, reportCount, uint16_t(length));
-		senderSsrc = htonl(ssrc);
-	}
-
-	inline static size_t sizeWithReportBlocks(uint8_t reportCount) {
-		return sizeof(header) + 4 + size_t(reportCount) * sizeof(RTCP_ReportBlock);
-	}
-
-	inline void log() const {
-		header.log();
-		PLOG_DEBUG << "RTCP RR: "
-		           << " SSRC=" << ntohl(senderSsrc);
-
-		for (unsigned i = 0; i < unsigned(header.reportCount()); i++) {
-			getReportBlock(i)->log();
-		}
-	}
-};
-
-struct RTCP_REMB {
-	RTCP_HEADER header;
-	SSRC senderSsrc;
-	SSRC mediaSourceSSRC;
-
-	// Unique identifier
-	const char id[4] = {'R', 'E', 'M', 'B'};
-
-	// Num SSRC, Br Exp, Br Mantissa (bit mask)
-	uint32_t bitrate;
-
-	SSRC ssrc[1];
-
-	[[nodiscard]] inline size_t getSize() const {
-		// "length" in packet is one less than the number of 32 bit words in the packet.
-		return sizeof(uint32_t) * (1 + size_t(header.length()));
-	}
-
-	inline void preparePacket(SSRC senderSsrc_, unsigned int numSSRC, unsigned int br) {
-		// Report Count becomes the format here.
-		header.prepareHeader(206, 15, 0);
-
-		// Always zero.
-		mediaSourceSSRC = 0;
+rtc::message_ptr RtcpReceivingSession::outgoing(rtc::message_ptr ptr) { return ptr; }
 
 
-		senderSsrc = htonl(senderSsrc_);
-		setBitrate(numSSRC, br);
-	}
-
-	inline void setBitrate(unsigned int numSSRC, unsigned int br) {
-		unsigned int exp = 0;
-		while (br > pow(2, 18) - 1) {
-			exp++;
-			br /= 2;
-		}
-
-		// "length" in packet is one less than the number of 32 bit words in the packet.
-		header.setLength(uint16_t(((sizeof(header) + 4 * 2 + 4 + 4) / 4) - 1 + numSSRC));
-
-		bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | br);
-	}
-
-	// TODO Make this work
-	//	  uint64_t getBitrate() const{
-	//		  uint32_t ntohed = ntohl(bitrate);
-	//		  uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1);
-	//		  unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u));
-	//		  return bitrate * pow(2,exp);
-	//	  }
-	//
-	//	  uint8_t getNumSSRCS() const {
-	//		  return ntohl(bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
-	//	  }
-
-	inline void setSSRC(uint8_t iterator, SSRC ssrc_) { ssrc[iterator] = htonl(ssrc_); }
-
-	inline void log() const {
-		header.log();
-		PLOG_DEBUG << "RTCP REMB: "
-		           << " SSRC=" << ntohl(senderSsrc);
-	}
-
-	static unsigned int sizeWithSSRCs(int numSSRC) {
-		return (sizeof(header) + 4 * 2 + 4 + 4) + sizeof(SSRC) * numSSRC;
-	}
-};
-
-#pragma pack(pop)
-
-void RtcpSession::onOutgoing(std::function<void(rtc::message_ptr)> cb) { mTxCallback = cb; }
-
-std::optional<rtc::message_ptr> RtcpSession::incoming(rtc::message_ptr ptr) {
+rtc::message_ptr RtcpReceivingSession::incoming(rtc::message_ptr ptr) {
 	if (ptr->type == rtc::Message::Type::Binary) {
 	if (ptr->type == rtc::Message::Type::Binary) {
 		auto rtp = reinterpret_cast<const RTP *>(ptr->data());
 		auto rtp = reinterpret_cast<const RTP *>(ptr->data());
 
 
@@ -339,12 +41,12 @@ std::optional<rtc::message_ptr> RtcpSession::incoming(rtc::message_ptr ptr) {
 		if (rtp->version() != 2) {
 		if (rtp->version() != 2) {
 			PLOG_WARNING << "RTP packet is not version 2";
 			PLOG_WARNING << "RTP packet is not version 2";
 
 
-			return std::nullopt;
+			return nullptr;
 		}
 		}
 		if (rtp->payloadType() == 201 || rtp->payloadType() == 200) {
 		if (rtp->payloadType() == 201 || rtp->payloadType() == 200) {
 			PLOG_WARNING << "RTP packet has a payload type indicating RR/SR";
 			PLOG_WARNING << "RTP packet has a payload type indicating RR/SR";
 
 
-			return std::nullopt;
+			return nullptr;
 		}
 		}
 
 
 		// TODO Implement the padding bit
 		// TODO Implement the padding bit
@@ -352,13 +54,7 @@ std::optional<rtc::message_ptr> RtcpSession::incoming(rtc::message_ptr ptr) {
 			PLOG_WARNING << "Padding processing not implemented";
 			PLOG_WARNING << "Padding processing not implemented";
 		}
 		}
 
 
-		mSsrc = ntohl(rtp->ssrc);
-
-		uint32_t seqNo = rtp->seqNumber();
-		// uint32_t rtpTS = rtp->getTS();
-
-		if (mGreatestSeqNo < seqNo)
-			mGreatestSeqNo = seqNo;
+		mSsrc = rtp->ssrc();
 
 
 		return ptr;
 		return ptr;
 	}
 	}
@@ -367,11 +63,11 @@ std::optional<rtc::message_ptr> RtcpSession::incoming(rtc::message_ptr ptr) {
 	auto rr = reinterpret_cast<const RTCP_RR *>(ptr->data());
 	auto rr = reinterpret_cast<const RTCP_RR *>(ptr->data());
 	if (rr->header.payloadType() == 201) {
 	if (rr->header.payloadType() == 201) {
 		// RR
 		// RR
-		mSsrc = rr->getSenderSSRC();
+		mSsrc = rr->senderSSRC();
 		rr->log();
 		rr->log();
 	} else if (rr->header.payloadType() == 200) {
 	} else if (rr->header.payloadType() == 200) {
 		// SR
 		// SR
-		mSsrc = rr->getSenderSSRC();
+		mSsrc = rr->senderSSRC();
 		auto sr = reinterpret_cast<const RTCP_SR *>(ptr->data());
 		auto sr = reinterpret_cast<const RTCP_SR *>(ptr->data());
 		mSyncRTPTS = sr->rtpTimestamp();
 		mSyncRTPTS = sr->rtpTimestamp();
 		mSyncNTPTS = sr->ntpTimestamp();
 		mSyncNTPTS = sr->ntpTimestamp();
@@ -382,28 +78,27 @@ std::optional<rtc::message_ptr> RtcpSession::incoming(rtc::message_ptr ptr) {
 		if (mRequestedBitrate > 0)
 		if (mRequestedBitrate > 0)
 			pushREMB(mRequestedBitrate);
 			pushREMB(mRequestedBitrate);
 	}
 	}
-	return std::nullopt;
+	return nullptr;
 }
 }
 
 
-void RtcpSession::requestBitrate(unsigned int newBitrate) {
+void RtcpReceivingSession::requestBitrate(unsigned int newBitrate) {
 	mRequestedBitrate = newBitrate;
 	mRequestedBitrate = newBitrate;
 
 
 	PLOG_DEBUG << "[GOOG-REMB] Requesting bitrate: " << newBitrate << std::endl;
 	PLOG_DEBUG << "[GOOG-REMB] Requesting bitrate: " << newBitrate << std::endl;
 	pushREMB(newBitrate);
 	pushREMB(newBitrate);
 }
 }
 
 
-void RtcpSession::pushREMB(unsigned int bitrate) {
+void RtcpReceivingSession::pushREMB(unsigned int bitrate) {
 	rtc::message_ptr msg =
 	rtc::message_ptr msg =
 	    rtc::make_message(RTCP_REMB::sizeWithSSRCs(1), rtc::Message::Type::Control);
 	    rtc::make_message(RTCP_REMB::sizeWithSSRCs(1), rtc::Message::Type::Control);
 	auto remb = reinterpret_cast<RTCP_REMB *>(msg->data());
 	auto remb = reinterpret_cast<RTCP_REMB *>(msg->data());
 	remb->preparePacket(mSsrc, 1, bitrate);
 	remb->preparePacket(mSsrc, 1, bitrate);
-	remb->setSSRC(0, mSsrc);
-	remb->log();
+	remb->setSsrc(0, mSsrc);
 
 
-	tx(msg);
+	send(msg);
 }
 }
 
 
-void RtcpSession::pushRR(unsigned int lastSR_delay) {
+void RtcpReceivingSession::pushRR(unsigned int lastSR_delay) {
 	auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
 	auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
 	auto rr = reinterpret_cast<RTCP_RR *>(msg->data());
 	auto rr = reinterpret_cast<RTCP_RR *>(msg->data());
 	rr->preparePacket(mSsrc, 1);
 	rr->preparePacket(mSsrc, 1);
@@ -411,16 +106,32 @@ void RtcpSession::pushRR(unsigned int lastSR_delay) {
 	                                     lastSR_delay);
 	                                     lastSR_delay);
 	rr->log();
 	rr->log();
 
 
-	tx(msg);
+	send(msg);
 }
 }
 
 
-void RtcpSession::tx(message_ptr msg) {
+bool RtcpReceivingSession::send(message_ptr msg) {
 	try {
 	try {
-		mTxCallback(msg);
+		outgoingCallback(std::move(msg));
+		return true;
 	} catch (const std::exception &e) {
 	} catch (const std::exception &e) {
 		LOG_DEBUG << "RTCP tx failed: " << e.what();
 		LOG_DEBUG << "RTCP tx failed: " << e.what();
 	}
 	}
+	return false;
 }
 }
 
 
-} // namespace rtc
+bool RtcpReceivingSession::requestKeyframe() {
+	pushPLI();
+	return true; // TODO Make this false when it is impossible (i.e. Opus).
+}
+
+void RtcpReceivingSession::pushPLI() {
+	auto msg = rtc::make_message(rtc::RTCP_PLI::size(), rtc::Message::Type::Control);
+	auto *pli = (rtc::RTCP_PLI *)msg->data();
+	pli->preparePacket(mSsrc);
+	send(msg);
+}
 
 
+void RtcpHandler::onOutgoing(const std::function<void(rtc::message_ptr)> &cb) {
+	this->outgoingCallback = synchronized_callback<rtc::message_ptr>(cb);
+}
+} // namespace rtc

+ 3 - 3
src/sctptransport.cpp

@@ -148,7 +148,7 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
 
 
 	struct sctp_paddrparams spp = {};
 	struct sctp_paddrparams spp = {};
 #if USE_PMTUD
 #if USE_PMTUD
-	// Enabled SCTP path MTU discovery
+	// Enable SCTP path MTU discovery
 	spp.spp_flags = SPP_PMTUD_ENABLE;
 	spp.spp_flags = SPP_PMTUD_ENABLE;
 #else
 #else
 	// Fall back to a safe MTU value.
 	// Fall back to a safe MTU value.
@@ -232,7 +232,7 @@ void SctpTransport::close() {
 
 
 void SctpTransport::connect() {
 void SctpTransport::connect() {
 	if (!mSock)
 	if (!mSock)
-		return;
+		throw std::logic_error("Attempted SCTP connect with closed socket");
 
 
 	PLOG_DEBUG << "SCTP connecting";
 	PLOG_DEBUG << "SCTP connecting";
 	changeState(State::Connecting);
 	changeState(State::Connecting);
@@ -305,7 +305,7 @@ void SctpTransport::incoming(message_ptr message) {
 	// to be sent on our side (i.e. the local INIT) before proceeding.
 	// to be sent on our side (i.e. the local INIT) before proceeding.
 	if (!mWrittenOnce) { // test the atomic boolean is not set first to prevent a lock contention
 	if (!mWrittenOnce) { // test the atomic boolean is not set first to prevent a lock contention
 		std::unique_lock lock(mWriteMutex);
 		std::unique_lock lock(mWriteMutex);
-		mWrittenCondition.wait(lock, [&]() { return mWrittenOnce || state() != State::Connected; });
+		mWrittenCondition.wait(lock, [&]() { return mWrittenOnce.load(); });
 	}
 	}
 
 
 	if (!message) {
 	if (!message) {

+ 1 - 1
src/sctptransport.hpp

@@ -100,7 +100,7 @@ private:
 
 
 	std::mutex mWriteMutex;
 	std::mutex mWriteMutex;
 	std::condition_variable mWrittenCondition;
 	std::condition_variable mWrittenCondition;
-	std::atomic<bool> mWritten = false; // written outside lock
+	std::atomic<bool> mWritten = false;     // written outside lock
 	std::atomic<bool> mWrittenOnce = false; // same
 	std::atomic<bool> mWrittenOnce = false; // same
 
 
 	binary mPartialMessage, mPartialNotification;
 	binary mPartialMessage, mPartialNotification;

+ 0 - 1
src/threadpool.cpp

@@ -75,4 +75,3 @@ std::function<void()> ThreadPool::dequeue() {
 }
 }
 
 
 } // namespace rtc
 } // namespace rtc
-

+ 7 - 7
src/threadpool.hpp

@@ -75,13 +75,13 @@ auto ThreadPool::enqueue(F &&f, Args &&... args) -> invoke_future_t<F, Args...>
 	using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
 	using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
 	auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
 	auto bound = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
 	auto task = std::make_shared<std::packaged_task<R()>>([bound = std::move(bound)]() mutable {
 	auto task = std::make_shared<std::packaged_task<R()>>([bound = std::move(bound)]() mutable {
-        try {
-            return bound();
-        } catch (const std::exception &e) {
-            PLOG_WARNING << e.what();
-            throw;
-        }
-    });
+		try {
+			return bound();
+		} catch (const std::exception &e) {
+			PLOG_WARNING << e.what();
+			throw;
+		}
+	});
 	std::future<R> result = task->get_future();
 	std::future<R> result = task->get_future();
 
 
 	mTasks.emplace([task = std::move(task), token = Init::Token()]() { return (*task)(); });
 	mTasks.emplace([task = std::move(task), token = Init::Token()]() { return (*task)(); });

+ 0 - 1
src/tls.cpp

@@ -127,4 +127,3 @@ bool check(SSL *ssl, int ret, const string &message) {
 } // namespace rtc::openssl
 } // namespace rtc::openssl
 
 
 #endif
 #endif
-

+ 2 - 2
src/tls.hpp

@@ -56,12 +56,12 @@ gnutls_datum_t make_datum(char *data, size_t size);
 #include <openssl/ssl.h>
 #include <openssl/ssl.h>
 
 
 #include <openssl/bio.h>
 #include <openssl/bio.h>
+#include <openssl/bn.h>
 #include <openssl/ec.h>
 #include <openssl/ec.h>
 #include <openssl/err.h>
 #include <openssl/err.h>
 #include <openssl/pem.h>
 #include <openssl/pem.h>
-#include <openssl/x509.h>
 #include <openssl/rsa.h>
 #include <openssl/rsa.h>
-#include <openssl/bn.h>
+#include <openssl/x509.h>
 
 
 #ifndef BIO_EOF
 #ifndef BIO_EOF
 #define BIO_EOF -1
 #define BIO_EOF -1

+ 8 - 9
src/tlstransport.cpp

@@ -62,10 +62,10 @@ TlsTransport::TlsTransport(shared_ptr<TcpTransport> lower, string host, state_ca
 		gnutls::check(gnutls_priority_set_direct(mSession, priorities, &err_pos),
 		gnutls::check(gnutls_priority_set_direct(mSession, priorities, &err_pos),
 		              "Failed to set TLS priorities");
 		              "Failed to set TLS priorities");
 
 
-       	PLOG_VERBOSE << "Server Name Indication: " << mHost;
+		PLOG_VERBOSE << "Server Name Indication: " << mHost;
 		gnutls_server_name_set(mSession, GNUTLS_NAME_DNS, mHost.data(), mHost.size());
 		gnutls_server_name_set(mSession, GNUTLS_NAME_DNS, mHost.data(), mHost.size());
 
 
- 		gnutls_session_set_ptr(mSession, this);
+		gnutls_session_set_ptr(mSession, this);
 		gnutls_transport_set_ptr(mSession, this);
 		gnutls_transport_set_ptr(mSession, this);
 		gnutls_transport_set_push_function(mSession, WriteCallback);
 		gnutls_transport_set_push_function(mSession, WriteCallback);
 		gnutls_transport_set_pull_function(mSession, ReadCallback);
 		gnutls_transport_set_pull_function(mSession, ReadCallback);
@@ -110,7 +110,7 @@ bool TlsTransport::send(message_ptr message) {
 
 
 	PLOG_VERBOSE << "Send size=" << message->size();
 	PLOG_VERBOSE << "Send size=" << message->size();
 
 
-	if(message->size() == 0)
+	if (message->size() == 0)
 		return true;
 		return true;
 
 
 	ssize_t ret;
 	ssize_t ret;
@@ -207,10 +207,10 @@ ssize_t TlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_
 	message_ptr &message = t->mIncomingMessage;
 	message_ptr &message = t->mIncomingMessage;
 	size_t &position = t->mIncomingMessagePosition;
 	size_t &position = t->mIncomingMessagePosition;
 
 
-	if(message && position >= message->size())
+	if (message && position >= message->size())
 		message.reset();
 		message.reset();
 
 
-	if(!message) {
+	if (!message) {
 		position = 0;
 		position = 0;
 		while (auto next = t->mIncomingQueue.pop()) {
 		while (auto next = t->mIncomingQueue.pop()) {
 			message = *next;
 			message = *next;
@@ -221,15 +221,14 @@ ssize_t TlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_
 		}
 		}
 	}
 	}
 
 
-	if(message) {
+	if (message) {
 		size_t available = message->size() - position;
 		size_t available = message->size() - position;
 		ssize_t len = std::min(maxlen, available);
 		ssize_t len = std::min(maxlen, available);
 		std::memcpy(data, message->data() + position, len);
 		std::memcpy(data, message->data() + position, len);
-		position+= len;
+		position += len;
 		gnutls_transport_set_errno(t->mSession, 0);
 		gnutls_transport_set_errno(t->mSession, 0);
 		return len;
 		return len;
-	}
-	else {
+	} else {
 		// Closed
 		// Closed
 		gnutls_transport_set_errno(t->mSession, 0);
 		gnutls_transport_set_errno(t->mSession, 0);
 		return 0;
 		return 0;

+ 41 - 19
src/track.cpp

@@ -33,7 +33,7 @@ string Track::mid() const { return mMediaDescription.mid(); }
 Description::Media Track::description() const { return mMediaDescription; }
 Description::Media Track::description() const { return mMediaDescription; }
 
 
 void Track::setDescription(Description::Media description) {
 void Track::setDescription(Description::Media description) {
-	if(description.mid() != mMediaDescription.mid())
+	if (description.mid() != mMediaDescription.mid())
 		throw std::logic_error("Media description mid does not match track mid");
 		throw std::logic_error("Media description mid does not match track mid");
 
 
 	mMediaDescription = std::move(description);
 	mMediaDescription = std::move(description);
@@ -58,6 +58,13 @@ std::optional<message_variant> Track::receive() {
 	return nullopt;
 	return nullopt;
 }
 }
 
 
+std::optional<message_variant> Track::peek() {
+	if (auto next = mRecvQueue.peek())
+		return to_variant(std::move(**next));
+
+	return nullopt;
+}
+
 bool Track::isOpen(void) const {
 bool Track::isOpen(void) const {
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 	return !mIsClosed && mDtlsSrtpTransport.lock();
 	return !mIsClosed && mDtlsSrtpTransport.lock();
@@ -72,9 +79,7 @@ size_t Track::maxMessageSize() const {
 	return 65535 - 12 - 4; // SRTP/UDP
 	return 65535 - 12 - 4; // SRTP/UDP
 }
 }
 
 
-size_t Track::availableAmount() const {
-	return mRecvQueue.amount();
-}
+size_t Track::availableAmount() const { return mRecvQueue.amount(); }
 
 
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
 void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
 void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
@@ -84,10 +89,20 @@ void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
 #endif
 #endif
 
 
 bool Track::outgoing(message_ptr message) {
 bool Track::outgoing(message_ptr message) {
+
+	if (mRtcpHandler) {
+		message = mRtcpHandler->outgoing(message);
+		if (!message)
+			return false;
+	}
+
 	auto direction = mMediaDescription.direction();
 	auto direction = mMediaDescription.direction();
-	if (direction == Description::Direction::RecvOnly ||
-	    direction == Description::Direction::Inactive)
-		throw std::runtime_error("Track media direction does not allow sending");
+	if ((direction == Description::Direction::RecvOnly ||
+	     direction == Description::Direction::Inactive) &&
+	    message->type != Message::Control) {
+		PLOG_WARNING << "Track media direction does not allow transmission, dropping";
+		return false;
+	}
 
 
 	if (mIsClosed)
 	if (mIsClosed)
 		throw std::runtime_error("Track is closed");
 		throw std::runtime_error("Track is closed");
@@ -112,11 +127,9 @@ void Track::incoming(message_ptr message) {
 		return;
 		return;
 
 
 	if (mRtcpHandler) {
 	if (mRtcpHandler) {
-		auto opt = mRtcpHandler->incoming(message);
-		if (!opt)
+		message = mRtcpHandler->incoming(message);
+		if (!message)
 			return;
 			return;
-
-		message = *opt;
 	}
 	}
 
 
 	auto direction = mMediaDescription.direction();
 	auto direction = mMediaDescription.direction();
@@ -124,6 +137,7 @@ void Track::incoming(message_ptr message) {
 	     direction == Description::Direction::Inactive) &&
 	     direction == Description::Direction::Inactive) &&
 	    message->type != Message::Control) {
 	    message->type != Message::Control) {
 		PLOG_WARNING << "Track media direction does not allow reception, dropping";
 		PLOG_WARNING << "Track media direction does not allow reception, dropping";
+		return;
 	}
 	}
 
 
 	// Tail drop if queue is full
 	// Tail drop if queue is full
@@ -135,21 +149,29 @@ void Track::incoming(message_ptr message) {
 }
 }
 
 
 void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
 void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
-	if (mRtcpHandler)
-		mRtcpHandler->onOutgoing(nullptr);
-
 	mRtcpHandler = std::move(handler);
 	mRtcpHandler = std::move(handler);
 	if (mRtcpHandler) {
 	if (mRtcpHandler) {
-		mRtcpHandler->onOutgoing([&]([[maybe_unused]] message_ptr message) {
+		mRtcpHandler->onOutgoing([&]([[maybe_unused]] const rtc::message_ptr &message) {
 #if RTC_ENABLE_MEDIA
 #if RTC_ENABLE_MEDIA
-			if (auto transport = mDtlsSrtpTransport.lock())
-				transport->sendMedia(message);
+			auto transport = mDtlsSrtpTransport.lock();
+			if (!transport)
+				throw std::runtime_error("Track transport is not open");
+
+			return transport->sendMedia(message);
 #else
 #else
-			PLOG_WARNING << "Ignoring RTCP send (not compiled with SRTP support)";
+			PLOG_WARNING << "Ignoring track send (not compiled with SRTP support)";
+			return false;
 #endif
 #endif
 		});
 		});
 	}
 	}
 }
 }
 
 
-} // namespace rtc
+bool Track::requestKeyframe() {
+	if (mRtcpHandler)
+		return mRtcpHandler->requestKeyframe();
+	return false;
+}
+
+std::shared_ptr<RtcpHandler> Track::getRtcpHandler() { return mRtcpHandler; }
 
 
+} // namespace rtc

+ 7 - 5
src/transport.hpp

@@ -36,8 +36,7 @@ public:
 	using state_callback = std::function<void(State state)>;
 	using state_callback = std::function<void(State state)>;
 
 
 	Transport(std::shared_ptr<Transport> lower = nullptr, state_callback callback = nullptr)
 	Transport(std::shared_ptr<Transport> lower = nullptr, state_callback callback = nullptr)
-	    : mLower(std::move(lower)), mStateChangeCallback(std::move(callback)) {
-	}
+	    : mLower(std::move(lower)), mStateChangeCallback(std::move(callback)) {}
 
 
 	virtual ~Transport() { stop(); }
 	virtual ~Transport() { stop(); }
 
 
@@ -48,15 +47,18 @@ public:
 			return false;
 			return false;
 
 
 		// We don't want incoming() to be called by the lower layer anymore
 		// We don't want incoming() to be called by the lower layer anymore
-		if (mLower)
+		if (mLower) {
+			PLOG_VERBOSE << "Unregistering incoming callback";
 			mLower->onRecv(nullptr);
 			mLower->onRecv(nullptr);
-
+		}
 		return true;
 		return true;
 	}
 	}
 
 
 	void registerIncoming() {
 	void registerIncoming() {
-		if (mLower)
+		if (mLower) {
+			PLOG_VERBOSE << "Registering incoming callback";
 			mLower->onRecv(std::bind(&Transport::incoming, this, _1));
 			mLower->onRecv(std::bind(&Transport::incoming, this, _1));
+		}
 	}
 	}
 
 
 	void onRecv(message_callback callback) { mRecvCallback = std::move(callback); }
 	void onRecv(message_callback callback) { mRecvCallback = std::move(callback); }

+ 0 - 2
src/verifiedtlstransport.cpp

@@ -28,7 +28,6 @@ using std::weak_ptr;
 
 
 namespace rtc {
 namespace rtc {
 
 
-
 VerifiedTlsTransport::VerifiedTlsTransport(shared_ptr<TcpTransport> lower, string host,
 VerifiedTlsTransport::VerifiedTlsTransport(shared_ptr<TcpTransport> lower, string host,
                                            state_callback callback)
                                            state_callback callback)
     : TlsTransport(std::move(lower), std::move(host), std::move(callback)) {
     : TlsTransport(std::move(lower), std::move(host), std::move(callback)) {
@@ -48,4 +47,3 @@ VerifiedTlsTransport::~VerifiedTlsTransport() {}
 } // namespace rtc
 } // namespace rtc
 
 
 #endif
 #endif
-

+ 16 - 4
src/websocket.cpp

@@ -125,13 +125,24 @@ size_t WebSocket::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }
 
 
 std::optional<message_variant> WebSocket::receive() {
 std::optional<message_variant> WebSocket::receive() {
 	while (auto next = mRecvQueue.tryPop()) {
 	while (auto next = mRecvQueue.tryPop()) {
-		message_ptr message = std::move(*next);
+		message_ptr message = *next;
 		if (message->type != Message::Control)
 		if (message->type != Message::Control)
 			return to_variant(std::move(*message));
 			return to_variant(std::move(*message));
 	}
 	}
 	return nullopt;
 	return nullopt;
 }
 }
 
 
+std::optional<message_variant> WebSocket::peek() {
+	while (auto next = mRecvQueue.peek()) {
+		message_ptr message = *next;
+		if (message->type != Message::Control)
+			return to_variant(std::move(*message));
+
+		mRecvQueue.tryPop();
+	}
+	return nullopt;
+}
+
 size_t WebSocket::availableAmount() const { return mRecvQueue.amount(); }
 size_t WebSocket::availableAmount() const { return mRecvQueue.amount(); }
 
 
 bool WebSocket::changeState(State state) { return mState.exchange(state) != state; }
 bool WebSocket::changeState(State state) { return mState.exchange(state) != state; }
@@ -240,12 +251,13 @@ shared_ptr<TlsTransport> WebSocket::initTlsTransport() {
 		if (!mConfig.disableTlsVerification) {
 		if (!mConfig.disableTlsVerification) {
 			PLOG_WARNING << "TLS certificate verification with root CA is not supported on Windows";
 			PLOG_WARNING << "TLS certificate verification with root CA is not supported on Windows";
 		}
 		}
-		transport = std::make_shared<TlsTransport>(lower, mHost, stateChangeCallback);
+		transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
 #else
 #else
 		if (mConfig.disableTlsVerification)
 		if (mConfig.disableTlsVerification)
-			transport = std::make_shared<TlsTransport>(lower, mHost, stateChangeCallback);
+			transport = std::make_shared<TlsTransport>(lower, mHostname, stateChangeCallback);
 		else
 		else
-			transport = std::make_shared<VerifiedTlsTransport>(lower, mHost, stateChangeCallback);
+			transport =
+			    std::make_shared<VerifiedTlsTransport>(lower, mHostname, stateChangeCallback);
 #endif
 #endif
 
 
 		std::atomic_store(&mTlsTransport, transport);
 		std::atomic_store(&mTlsTransport, transport);

+ 1 - 1
src/wstransport.cpp

@@ -17,9 +17,9 @@
  */
  */
 
 
 #include "wstransport.hpp"
 #include "wstransport.hpp"
+#include "base64.hpp"
 #include "tcptransport.hpp"
 #include "tcptransport.hpp"
 #include "tlstransport.hpp"
 #include "tlstransport.hpp"
-#include "base64.hpp"
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 

+ 33 - 2
test/connectivity.cpp

@@ -156,11 +156,11 @@ void test_connectivity() {
 		cout << "Remote address 2: " << *addr << endl;
 		cout << "Remote address 2: " << *addr << endl;
 
 
 	Candidate local, remote;
 	Candidate local, remote;
-	if(pc1->getSelectedCandidatePair(&local, &remote)) {
+	if (pc1->getSelectedCandidatePair(&local, &remote)) {
 		cout << "Local candidate 1:  " << local << endl;
 		cout << "Local candidate 1:  " << local << endl;
 		cout << "Remote candidate 1: " << remote << endl;
 		cout << "Remote candidate 1: " << remote << endl;
 	}
 	}
-	if(pc2->getSelectedCandidatePair(&local, &remote)) {
+	if (pc2->getSelectedCandidatePair(&local, &remote)) {
 		cout << "Local candidate 2:  " << local << endl;
 		cout << "Local candidate 2:  " << local << endl;
 		cout << "Remote candidate 2: " << remote << endl;
 		cout << "Remote candidate 2: " << remote << endl;
 	}
 	}
@@ -208,6 +208,37 @@ void test_connectivity() {
 	    attempts--)
 	    attempts--)
 		this_thread::sleep_for(1s);
 		this_thread::sleep_for(1s);
 
 
+	if (!asecond2 || !asecond2->isOpen() || !second1->isOpen())
+		throw runtime_error("Second DataChannel is not open");
+
+	// Try to open a negotiated channel
+	DataChannelInit init;
+	init.negotiated = true;
+	init.id = 42;
+	auto negotiated1 = pc1->createDataChannel("negotiated", init);
+	auto negotiated2 = pc2->createDataChannel("negoctated", init);
+
+	if (!negotiated1->isOpen() || !negotiated2->isOpen())
+		throw runtime_error("Negociated DataChannel is not open");
+
+	std::atomic<bool> received = false;
+	negotiated2->onMessage([&received](const variant<binary, string> &message) {
+		if (holds_alternative<string>(message)) {
+			cout << "Second Message 2: " << get<string>(message) << endl;
+			received = true;
+		}
+	});
+
+	negotiated1->send("Hello from negotiated channel");
+
+	// Wait a bit
+	attempts = 5;
+	while (!received && attempts--)
+		this_thread::sleep_for(1s);
+
+	if (!received)
+		throw runtime_error("Negociated DataChannel failed");
+
 	// Delay close of peer 2 to check closing works properly
 	// Delay close of peer 2 to check closing works properly
 	pc1->close();
 	pc1->close();
 	this_thread::sleep_for(1s);
 	this_thread::sleep_for(1s);

+ 1 - 1
test/track.cpp

@@ -133,7 +133,7 @@ void test_track() {
 		this_thread::sleep_for(1s);
 		this_thread::sleep_for(1s);
 
 
 	if (!at2 || !at2->isOpen() || !t1->isOpen())
 	if (!at2 || !at2->isOpen() || !t1->isOpen())
-		throw runtime_error("Renegociated track is not open");
+		throw runtime_error("Renegotiated track is not open");
 
 
 	// TODO: Test sending RTP packets in track
 	// TODO: Test sending RTP packets in track
 
 

+ 4 - 5
test/websocket.cpp

@@ -20,11 +20,11 @@
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 
+#include <atomic>
 #include <chrono>
 #include <chrono>
 #include <iostream>
 #include <iostream>
 #include <memory>
 #include <memory>
 #include <thread>
 #include <thread>
-#include <atomic>
 
 
 using namespace rtc;
 using namespace rtc;
 using namespace std;
 using namespace std;
@@ -56,14 +56,14 @@ void test_websocket() {
 	ws->onMessage([&received, &myMessage](variant<binary, string> message) {
 	ws->onMessage([&received, &myMessage](variant<binary, string> message) {
 		if (holds_alternative<string>(message)) {
 		if (holds_alternative<string>(message)) {
 			string str = std::move(get<string>(message));
 			string str = std::move(get<string>(message));
-			if((received = (str == myMessage)))
+			if ((received = (str == myMessage)))
 				cout << "WebSocket: Received expected message" << endl;
 				cout << "WebSocket: Received expected message" << endl;
 			else
 			else
 				cout << "WebSocket: Received UNEXPECTED message" << endl;
 				cout << "WebSocket: Received UNEXPECTED message" << endl;
 		}
 		}
 	});
 	});
 
 
-	ws->open("wss://echo.websocket.org/");
+	ws->open("wss://echo.websocket.org:443/");
 
 
 	int attempts = 10;
 	int attempts = 10;
 	while ((!ws->isOpen() || !received) && attempts--)
 	while ((!ws->isOpen() || !received) && attempts--)
@@ -72,7 +72,7 @@ void test_websocket() {
 	if (!ws->isOpen())
 	if (!ws->isOpen())
 		throw runtime_error("WebSocket is not open");
 		throw runtime_error("WebSocket is not open");
 
 
-	if(!received)
+	if (!received)
 		throw runtime_error("Expected message not received");
 		throw runtime_error("Expected message not received");
 
 
 	ws->close();
 	ws->close();
@@ -86,4 +86,3 @@ void test_websocket() {
 }
 }
 
 
 #endif
 #endif
-