From f1da765a02981af5fa7d13d27d54e633464a3936 Mon Sep 17 00:00:00 2001 From: Takeru Ohta Date: Wed, 27 Mar 2024 11:43:13 +0900 Subject: [PATCH 01/26] Add empty payload packet handling to H264DEpacketizer --- src/h264rtpdepacketizer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/h264rtpdepacketizer.cpp b/src/h264rtpdepacketizer.cpp index 024a46b3b..36a5061f0 100644 --- a/src/h264rtpdepacketizer.cpp +++ b/src/h264rtpdepacketizer.cpp @@ -42,6 +42,12 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, auto pktParsed = reinterpret_cast(pkt->data()); auto headerSize = sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize(); + + if (pkt->size() == headerSize) { + PLOG_VERBOSE << "H.264 RTP packet has empty payload"; + continue; + } + auto nalUnitHeader = NalUnitHeader{std::to_integer(pkt->at(headerSize))}; if (fua_buffer.size() != 0 || nalUnitHeader.unitType() == naluTypeFUA) { From c5c80b8760af3ec767314e18489193a18736fdaa Mon Sep 17 00:00:00 2001 From: Takeru Ohta Date: Wed, 27 Mar 2024 14:41:03 +0900 Subject: [PATCH 02/26] Add padding handling --- src/h264rtpdepacketizer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/h264rtpdepacketizer.cpp b/src/h264rtpdepacketizer.cpp index 36a5061f0..e897ba664 100644 --- a/src/h264rtpdepacketizer.cpp +++ b/src/h264rtpdepacketizer.cpp @@ -42,10 +42,15 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, auto pktParsed = reinterpret_cast(pkt->data()); auto headerSize = sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize(); + auto paddingSize = 0; - if (pkt->size() == headerSize) { + if (pktParsed->padding()) { + paddingSize = std::to_integer(pkt->at(pkt->size() - 1)); + } + + if (pkt->size() == headerSize + paddingSize) { PLOG_VERBOSE << "H.264 RTP packet has empty payload"; - continue; + continue; } auto nalUnitHeader = NalUnitHeader{std::to_integer(pkt->at(headerSize))}; From 41320cc08ed788be6ddbbedde458241e8291a399 Mon Sep 17 00:00:00 2001 From: sbarrac Date: Mon, 1 Apr 2024 22:23:09 +0100 Subject: [PATCH 03/26] Added playout delay extension support --- include/rtc/rtc.h | 3 +++ include/rtc/rtppacketizationconfig.hpp | 8 ++++++++ src/capi.cpp | 3 +++ src/rtppacketizer.cpp | 16 ++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index e37ccec86..1476b6a9a 100644 --- a/include/rtc/rtc.h +++ b/include/rtc/rtc.h @@ -341,6 +341,9 @@ typedef struct { // AV1 only rtcObuPacketization obuPacketization; // OBU paketization for AV1 samples + uint8_t playoutDelayId; + uint16_t playoutDelayMin; + uint16_t playoutDelayMax; } rtcPacketizerInit; // Deprecated, do not use diff --git a/include/rtc/rtppacketizationconfig.hpp b/include/rtc/rtppacketizationconfig.hpp index 0e6dcada2..ace13063e 100644 --- a/include/rtc/rtppacketizationconfig.hpp +++ b/include/rtc/rtppacketizationconfig.hpp @@ -61,6 +61,14 @@ class RTC_CPP_EXPORT RtpPacketizationConfig { uint8_t ridId = 0; optional rid; + // the negotiated ID of the playout delay header extension + // https://webrtc.googlesource.com/src/+/main/docs/native-code/rtp-hdrext/playout-delay/README.md + uint8_t playoutDelayId; + + // Minimum/maxiumum playout delay, in 10ms intervals. A value of 10 would equal a 100ms delay + uint16_t playoutDelayMin; + uint16_t playoutDelayMax; + /// Construct RTP configuration used in packetization process /// @param ssrc SSRC of source /// @param cname CNAME of source diff --git a/src/capi.cpp b/src/capi.cpp index b6942214a..6f8c790d2 100644 --- a/src/capi.cpp +++ b/src/capi.cpp @@ -275,6 +275,9 @@ createRtpPacketizationConfig(const rtcPacketizationHandlerInit *init) { init->payloadType, init->clockRate); config->sequenceNumber = init->sequenceNumber; config->timestamp = init->timestamp; + config->playoutDelayId = init->playoutDelayId; + config->playoutDelayMin = init->playoutDelayMin; + config->playoutDelayMax = init->playoutDelayMax; return config; } diff --git a/src/rtppacketizer.cpp b/src/rtppacketizer.cpp index ba7048ac5..fa9d65ee6 100644 --- a/src/rtppacketizer.cpp +++ b/src/rtppacketizer.cpp @@ -31,6 +31,11 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { if (setVideoRotation) rtpExtHeaderSize += 2; + const bool setPlayoutDelay = (rtpConfig->playoutDelayId > 0 && rtpConfig->playoutDelayId < 15); + + if (setPlayoutDelay) + rtpExtHeaderSize += 1; + if (rtpConfig->mid.has_value()) rtpExtHeaderSize += (1 + rtpConfig->mid->length()); @@ -85,6 +90,17 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { reinterpret_cast(rtpConfig->rid->c_str()), rtpConfig->rid->length()); } + + if (setPlayoutDelay) { + // 12 bits for min + 12 bits for max + char data[] = {rtpConfig->playoutDelayMin >> 4, + (char)(rtpConfig->playoutDelayMin << 4) | + (char)(rtpConfig->playoutDelayMax >> 8), + rtpConfig->playoutDelayMax}; + + extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, (byte *)data, 3); + offset += 4; + } } rtp->preparePacket(); From 2f13f9e294dbfcdba7fc1ac38c9374d52cf84b68 Mon Sep 17 00:00:00 2001 From: sbarrac Date: Mon, 1 Apr 2024 23:02:38 +0100 Subject: [PATCH 04/26] Fixed data conversion --- src/rtppacketizer.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rtppacketizer.cpp b/src/rtppacketizer.cpp index fa9d65ee6..3ca6677ca 100644 --- a/src/rtppacketizer.cpp +++ b/src/rtppacketizer.cpp @@ -92,11 +92,15 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { } if (setPlayoutDelay) { + uint16_t min = rtpConfig->playoutDelayMin & 0xFFF; + uint16_t max = rtpConfig->playoutDelayMax & 0xFFF; + // 12 bits for min + 12 bits for max - char data[] = {rtpConfig->playoutDelayMin >> 4, - (char)(rtpConfig->playoutDelayMin << 4) | - (char)(rtpConfig->playoutDelayMax >> 8), - rtpConfig->playoutDelayMax}; + char data[] = { + (min >> 4) & 0xFF, + ((min & 0xF) << 4) | ((max >> 8) & 0xF), + max & 0xFF + }; extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, (byte *)data, 3); offset += 4; From a17ef4ec2905815138c53639f86bba1abadf1767 Mon Sep 17 00:00:00 2001 From: sbarrac Date: Mon, 1 Apr 2024 23:22:09 +0100 Subject: [PATCH 05/26] Add casts --- src/rtppacketizer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rtppacketizer.cpp b/src/rtppacketizer.cpp index 3ca6677ca..e39c8bb3e 100644 --- a/src/rtppacketizer.cpp +++ b/src/rtppacketizer.cpp @@ -97,9 +97,9 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { // 12 bits for min + 12 bits for max char data[] = { - (min >> 4) & 0xFF, - ((min & 0xF) << 4) | ((max >> 8) & 0xF), - max & 0xFF + static_cast((min >> 4) & 0xFF), + static_cast(((min & 0xF) << 4) | ((max >> 8) & 0xF)), + static_cast(max & 0xFF) }; extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, (byte *)data, 3); From 4b9504863a1307870214794d03540183e1c28afe Mon Sep 17 00:00:00 2001 From: Denis Miller <8264513+dmllr@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:29:28 +0900 Subject: [PATCH 06/26] allow cmake to use imported mbedtls library --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93727c63c..4967f1639 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -371,7 +371,9 @@ if (USE_GNUTLS) target_link_libraries(datachannel-static PRIVATE Nettle::Nettle) endif() elseif(USE_MBEDTLS) - find_package(MbedTLS 3 REQUIRED) + if(NOT TARGET MbedTLS::MbedTLS) + find_package(MbedTLS 3 REQUIRED) + endif() target_compile_definitions(datachannel PRIVATE USE_MBEDTLS=1) target_compile_definitions(datachannel-static PRIVATE USE_MBEDTLS=1) target_link_libraries(datachannel PRIVATE MbedTLS::MbedTLS) From e9060bf3a39cbc0b1264e5db0c903a1a2164972b Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 10 Apr 2024 14:10:14 -0500 Subject: [PATCH 07/26] Include payloadType in FrameInfo So that consumers of onFrame can tell what codec the frame is in and know what decoder to use. --- include/rtc/frameinfo.hpp | 3 ++- include/rtc/h264rtpdepacketizer.hpp | 2 +- src/h264rtpdepacketizer.cpp | 8 +++++--- src/rtpdepacketizer.cpp | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/rtc/frameinfo.hpp b/include/rtc/frameinfo.hpp index 55afdbcfa..022c430dd 100644 --- a/include/rtc/frameinfo.hpp +++ b/include/rtc/frameinfo.hpp @@ -14,7 +14,8 @@ namespace rtc { struct RTC_CPP_EXPORT FrameInfo { - FrameInfo(uint32_t timestamp) : timestamp(timestamp){}; + FrameInfo(uint8_t payloadType, uint32_t timestamp) : payloadType(payloadType), timestamp(timestamp){}; + uint8_t payloadType; // Indicates codec of the frame uint32_t timestamp = 0; // RTP Timestamp }; diff --git a/include/rtc/h264rtpdepacketizer.hpp b/include/rtc/h264rtpdepacketizer.hpp index bf4505687..5fe09c916 100644 --- a/include/rtc/h264rtpdepacketizer.hpp +++ b/include/rtc/h264rtpdepacketizer.hpp @@ -33,7 +33,7 @@ class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler { std::vector mRtpBuffer; message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt, - uint32_t timestamp); + uint8_t payloadType, uint32_t timestamp); }; } // namespace rtc diff --git a/src/h264rtpdepacketizer.cpp b/src/h264rtpdepacketizer.cpp index 024a46b3b..32052d291 100644 --- a/src/h264rtpdepacketizer.cpp +++ b/src/h264rtpdepacketizer.cpp @@ -32,10 +32,10 @@ const uint8_t naluTypeSTAPA = 24; const uint8_t naluTypeFUA = 28; message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, - message_vector::iterator end, uint32_t timestamp) { + message_vector::iterator end, uint8_t payloadType, uint32_t timestamp) { message_vector out = {}; auto fua_buffer = std::vector{}; - auto frameInfo = std::make_shared(timestamp); + auto frameInfo = std::make_shared(payloadType, timestamp); for (auto it = begin; it != end; it++) { auto pkt = it->get(); @@ -111,6 +111,7 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb messages.end()); while (mRtpBuffer.size() != 0) { + uint8_t payload_type = 0; uint32_t current_timestamp = 0; size_t packets_in_timestamp = 0; @@ -119,6 +120,7 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb if (current_timestamp == 0) { current_timestamp = p->timestamp(); + payload_type = p->payloadType(); // should all be the same for data of the same codec } else if (current_timestamp != p->timestamp()) { break; } @@ -133,7 +135,7 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb auto begin = mRtpBuffer.begin(); auto end = mRtpBuffer.begin() + (packets_in_timestamp - 1); - auto frames = buildFrames(begin, end + 1, current_timestamp); + auto frames = buildFrames(begin, end + 1, payload_type, current_timestamp); messages.insert(messages.end(), frames.begin(), frames.end()); mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp); } diff --git a/src/rtpdepacketizer.cpp b/src/rtpdepacketizer.cpp index 9aa7cebf8..59d907444 100644 --- a/src/rtpdepacketizer.cpp +++ b/src/rtpdepacketizer.cpp @@ -36,7 +36,7 @@ void RtpDepacketizer::incoming([[maybe_unused]] message_vector &messages, auto headerSize = sizeof(rtc::RtpHeader) + pkt->csrcCount() + pkt->getExtensionHeaderSize(); result.push_back(make_message(message->begin() + headerSize, message->end(), Message::Binary, 0, nullptr, - std::make_shared(pkt->timestamp()))); + std::make_shared(pkt->payloadType(), pkt->timestamp()))); } messages.swap(result); From 82f996246809f4a3e86c9a693a0731f6738012f9 Mon Sep 17 00:00:00 2001 From: Ray Chung Date: Wed, 10 Apr 2024 20:10:29 +0000 Subject: [PATCH 08/26] Fix misaligned dereference in H264 Parser Use memcpy instead of direct dereference to avoid misaligned access. --- examples/streamer/h264fileparser.cpp | 5 ++++- src/h264rtppacketizer.cpp | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/streamer/h264fileparser.cpp b/examples/streamer/h264fileparser.cpp index f452b845f..245b7afc0 100644 --- a/examples/streamer/h264fileparser.cpp +++ b/examples/streamer/h264fileparser.cpp @@ -11,6 +11,7 @@ #include "rtc/rtc.hpp" #include +#include #ifdef _WIN32 #include @@ -29,7 +30,9 @@ void H264FileParser::loadNextSample() { while (i < sample.size()) { assert(i + 4 < sample.size()); auto lengthPtr = (uint32_t *) (sample.data() + i); - uint32_t length = ntohl(*lengthPtr); + uint32_t length; + std::memcpy(&length, lengthPtr, sizeof(uint32_t)); + length = ntohl(length); auto naluStartIndex = i + 4; auto naluEndIndex = naluStartIndex + length; assert(naluEndIndex <= sample.size()); diff --git a/src/h264rtppacketizer.cpp b/src/h264rtppacketizer.cpp index cb01d00b2..aa923cfa5 100644 --- a/src/h264rtppacketizer.cpp +++ b/src/h264rtppacketizer.cpp @@ -32,8 +32,9 @@ shared_ptr H264RtpPacketizer::splitMessage(binary_ptr message) { LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!"; break; } - auto lengthPtr = (uint32_t *)(message->data() + index); - uint32_t length = ntohl(*lengthPtr); + uint32_t length; + std::memcpy(&length, message->data() + index, sizeof(uint32_t)); + length = ntohl(length); auto naluStartIndex = index + 4; auto naluEndIndex = naluStartIndex + length; From 3b3a61d90c441dac2b6afb28d4dbed2b08dca40a Mon Sep 17 00:00:00 2001 From: Denis Miller <8264513+dmllr@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:09:12 +0900 Subject: [PATCH 09/26] fix MbedTLS usage bugs --- CMakeLists.txt | 4 +++- examples/streamer/h264fileparser.cpp | 4 ++-- src/impl/tls.cpp | 2 +- src/impl/tlstransport.cpp | 1 + src/impl/verifiedtlstransport.cpp | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4967f1639..cd8f0476a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,7 +342,9 @@ else() target_link_libraries(datachannel PRIVATE libSRTP::srtp2) target_link_libraries(datachannel-static PRIVATE libSRTP::srtp2) else() - add_subdirectory(deps/libsrtp EXCLUDE_FROM_ALL) + if(NOT TARGET srtp2) + add_subdirectory(deps/libsrtp EXCLUDE_FROM_ALL) + endif() 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) diff --git a/examples/streamer/h264fileparser.cpp b/examples/streamer/h264fileparser.cpp index f452b845f..cc8a6aa3f 100644 --- a/examples/streamer/h264fileparser.cpp +++ b/examples/streamer/h264fileparser.cpp @@ -50,8 +50,8 @@ void H264FileParser::loadNextSample() { } } -vector H264FileParser::initialNALUS() { - vector units{}; +vector H264FileParser::initialNALUS() { + vector units{}; if (previousUnitType7.has_value()) { auto nalu = previousUnitType7.value(); units.insert(units.end(), nalu.begin(), nalu.end()); diff --git a/src/impl/tls.cpp b/src/impl/tls.cpp index 650f90743..bc9d7ba1a 100644 --- a/src/impl/tls.cpp +++ b/src/impl/tls.cpp @@ -101,7 +101,7 @@ bool check(int ret, const string &message) { if (ret < 0) { if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS || - ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) return false; const size_t bufferSize = 1024; diff --git a/src/impl/tlstransport.cpp b/src/impl/tlstransport.cpp index de5dd4b44..8f66d200f 100644 --- a/src/impl/tlstransport.cpp +++ b/src/impl/tlstransport.cpp @@ -323,6 +323,7 @@ TlsTransport::TlsTransport(variant, shared_ptr(cacert->c_str()), - cacert->size())); + cacert->size() + 1)); } mbedtls_ssl_conf_ca_chain(&mConf, &mCaCert, NULL); } From 9e3871b65aece0d9a650940863c6ab55fb23d800 Mon Sep 17 00:00:00 2001 From: Ray Chung Date: Sun, 14 Apr 2024 13:12:02 +0000 Subject: [PATCH 10/26] Fix misaligned dereference in H265RtpPacketizer --- src/h265rtppacketizer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/h265rtppacketizer.cpp b/src/h265rtppacketizer.cpp index 5776dafa8..e4c60c79a 100644 --- a/src/h265rtppacketizer.cpp +++ b/src/h265rtppacketizer.cpp @@ -32,8 +32,9 @@ shared_ptr H265RtpPacketizer::splitMessage(binary_ptr message) { LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!"; break; } - auto lengthPtr = (uint32_t *)(message->data() + index); - uint32_t length = ntohl(*lengthPtr); + uint32_t length; + std::memcpy(&length, message->data() + index, sizeof(uint32_t)); + length = ntohl(length); auto naluStartIndex = index + 4; auto naluEndIndex = naluStartIndex + length; From fe7bec8594f116df5dc7efe17f4321541fffe501 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Mon, 15 Apr 2024 12:32:41 +0200 Subject: [PATCH 11/26] Updated libjuice to v1.4.0 --- deps/libjuice | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libjuice b/deps/libjuice index 0b6f958ba..ac0fc81d4 160000 --- a/deps/libjuice +++ b/deps/libjuice @@ -1 +1 @@ -Subproject commit 0b6f958baba55e1a4eb31ec2137f62b2e07382ae +Subproject commit ac0fc81d41be19d58195d6a0fc28c4c5b67a1626 From 5b71775c8eb65f7f05a8af9130fd319c9eab36c8 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Thu, 11 Apr 2024 22:27:48 +0200 Subject: [PATCH 12/26] Merge pull request #1154 from dmllr/master Fix MbedTLS usage bugs and allow cmake to use imported mbedtls library --- CMakeLists.txt | 8 ++++++-- examples/streamer/h264fileparser.cpp | 4 ++-- src/impl/tls.cpp | 2 +- src/impl/tlstransport.cpp | 1 + src/impl/verifiedtlstransport.cpp | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cdbfe596d..9748f1fc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,7 +331,9 @@ else() target_link_libraries(datachannel PRIVATE libSRTP::srtp2) target_link_libraries(datachannel-static PRIVATE libSRTP::srtp2) else() - add_subdirectory(deps/libsrtp EXCLUDE_FROM_ALL) + if(NOT TARGET srtp2) + add_subdirectory(deps/libsrtp EXCLUDE_FROM_ALL) + endif() 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) @@ -360,7 +362,9 @@ if (USE_GNUTLS) target_link_libraries(datachannel-static PRIVATE Nettle::Nettle) endif() elseif(USE_MBEDTLS) - find_package(MbedTLS 3 REQUIRED) + if(NOT TARGET MbedTLS::MbedTLS) + find_package(MbedTLS 3 REQUIRED) + endif() target_compile_definitions(datachannel PRIVATE USE_MBEDTLS=1) target_compile_definitions(datachannel-static PRIVATE USE_MBEDTLS=1) target_link_libraries(datachannel PRIVATE MbedTLS::MbedTLS) diff --git a/examples/streamer/h264fileparser.cpp b/examples/streamer/h264fileparser.cpp index f452b845f..cc8a6aa3f 100644 --- a/examples/streamer/h264fileparser.cpp +++ b/examples/streamer/h264fileparser.cpp @@ -50,8 +50,8 @@ void H264FileParser::loadNextSample() { } } -vector H264FileParser::initialNALUS() { - vector units{}; +vector H264FileParser::initialNALUS() { + vector units{}; if (previousUnitType7.has_value()) { auto nalu = previousUnitType7.value(); units.insert(units.end(), nalu.begin(), nalu.end()); diff --git a/src/impl/tls.cpp b/src/impl/tls.cpp index 650f90743..bc9d7ba1a 100644 --- a/src/impl/tls.cpp +++ b/src/impl/tls.cpp @@ -101,7 +101,7 @@ bool check(int ret, const string &message) { if (ret < 0) { if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS || - ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) return false; const size_t bufferSize = 1024; diff --git a/src/impl/tlstransport.cpp b/src/impl/tlstransport.cpp index de5dd4b44..8f66d200f 100644 --- a/src/impl/tlstransport.cpp +++ b/src/impl/tlstransport.cpp @@ -323,6 +323,7 @@ TlsTransport::TlsTransport(variant, shared_ptr(cacert->c_str()), - cacert->size())); + cacert->size() + 1)); } mbedtls_ssl_conf_ca_chain(&mConf, &mCaCert, NULL); } From 9b2d169f0714dfaefd259d36e0590a3044fd4dba Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Wed, 17 Apr 2024 13:25:22 +0200 Subject: [PATCH 13/26] Bumped version to 0.20.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9748f1fc3..b3c500629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.7) project(libdatachannel - VERSION 0.20.2 + VERSION 0.20.3 LANGUAGES CXX) set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets") From 1f4d08ab7e038e3e773d10e38e3044278661d559 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Wed, 17 Apr 2024 13:32:52 +0200 Subject: [PATCH 14/26] Updated version.h --- include/rtc/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rtc/version.h b/include/rtc/version.h index 11c26fcd7..ff74b435b 100644 --- a/include/rtc/version.h +++ b/include/rtc/version.h @@ -3,7 +3,7 @@ #define RTC_VERSION_MAJOR 0 #define RTC_VERSION_MINOR 20 -#define RTC_VERSION_PATCH 2 -#define RTC_VERSION "0.20.2" +#define RTC_VERSION_PATCH 3 +#define RTC_VERSION "0.20.3" #endif From 0620dda6e5726c6c17b7b5f2a3ff7bd7dd88e2ec Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Tue, 9 Apr 2024 22:32:50 -0400 Subject: [PATCH 15/26] Add disableAutoGathering When enabled PeerConnection::gatherLocalCandidates must be called explicitly to start the gathering process. This new API enabled ICE Servers to be used with WHIP/WHEP. For WHIP/WHEP the ICE Servers is only provided after the client makes the initial offer. --- include/rtc/configuration.hpp | 1 + include/rtc/peerconnection.hpp | 1 + src/impl/icetransport.cpp | 191 ++++++++++++++++++++------------- src/impl/icetransport.hpp | 5 +- src/peerconnection.cpp | 15 ++- 5 files changed, 137 insertions(+), 76 deletions(-) diff --git a/include/rtc/configuration.hpp b/include/rtc/configuration.hpp index 41bea91d7..873061f91 100644 --- a/include/rtc/configuration.hpp +++ b/include/rtc/configuration.hpp @@ -75,6 +75,7 @@ struct RTC_CPP_EXPORT Configuration { bool enableIceTcp = false; // libnice only bool enableIceUdpMux = false; // libjuice only bool disableAutoNegotiation = false; + bool disableAutoGathering = false; bool forceMediaTransport = false; // Port range diff --git a/include/rtc/peerconnection.hpp b/include/rtc/peerconnection.hpp index 86ea410cd..9e49f80e1 100644 --- a/include/rtc/peerconnection.hpp +++ b/include/rtc/peerconnection.hpp @@ -93,6 +93,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat { void setLocalDescription(Description::Type type = Description::Type::Unspec); void setRemoteDescription(Description description); void addRemoteCandidate(Candidate candidate); + void gatherLocalCandidates(std::vector additionalIceServers = {}); void setMediaHandler(shared_ptr handler); shared_ptr getMediaHandler(); diff --git a/src/impl/icetransport.cpp b/src/impl/icetransport.cpp index 269d16a3f..a2c56b0aa 100644 --- a/src/impl/icetransport.cpp +++ b/src/impl/icetransport.cpp @@ -117,27 +117,6 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi } } - juice_turn_server_t turn_servers[MAX_TURN_SERVERS_COUNT]; - std::memset(turn_servers, 0, sizeof(turn_servers)); - - // Add TURN servers - int k = 0; - for (auto &server : servers) { - if (!server.hostname.empty() && server.type == IceServer::Type::Turn) { - if (server.port == 0) - server.port = 3478; // TURN UDP port - PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\""; - turn_servers[k].host = server.hostname.c_str(); - turn_servers[k].username = server.username.c_str(); - turn_servers[k].password = server.password.c_str(); - turn_servers[k].port = server.port; - if (++k >= MAX_TURN_SERVERS_COUNT) - break; - } - } - jconfig.turn_servers = k > 0 ? turn_servers : nullptr; - jconfig.turn_servers_count = k; - // Bind address if (config.bindAddress) { jconfig.bind_address = config.bindAddress->c_str(); @@ -154,6 +133,44 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi mAgent = decltype(mAgent)(juice_create(&jconfig), juice_destroy); if (!mAgent) throw std::runtime_error("Failed to create the ICE agent"); + + // Filter STUN servers + servers.erase(std::remove_if(servers.begin(), servers.end(), + [](const IceServer &server) { + return server.hostname.empty() || + server.type != IceServer::Type::Turn; + }), + servers.end()); + + // Add TURN servers + for (auto &server : servers) { + if (mTURNServersAdded++ >= MAX_TURN_SERVERS_COUNT) { + break; + } + + addIceServer(server); + } +} + +void IceTransport::addIceServer(IceServer server) { + if (server.hostname.empty() || server.type != IceServer::Type::Turn) { + PLOG_WARNING << "Only TURN servers are supported as additional ICE servers"; + return; + } + + if (server.port == 0) + server.port = 3478; // TURN UDP port + + PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\""; + juice_turn_server_t turn_server = {}; + turn_server.host = server.hostname.c_str(); + turn_server.username = server.username.c_str(); + turn_server.password = server.password.c_str(); + turn_server.port = server.port; + + if (juice_add_turn_server(mAgent.get(), &turn_server) != 0) { + throw std::runtime_error("Failed to add TURN server"); + } } IceTransport::~IceTransport() { @@ -210,8 +227,17 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0; } -void IceTransport::gatherLocalCandidates(string mid) { +void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); + std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); + + for (auto &server : additionalIceServers) { + if (mTURNServersAdded++ >= MAX_TURN_SERVERS_COUNT) { + break; + } + + addIceServer(server); + } // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); @@ -533,59 +559,17 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi break; } + // Filter STUN servers + servers.erase(std::remove_if(servers.begin(), servers.end(), + [](const IceServer &server) { + return server.hostname.empty() || + server.type != IceServer::Type::Turn; + }), + servers.end()); + // Add TURN servers for (auto &server : servers) { - if (server.hostname.empty()) - continue; - if (server.type != IceServer::Type::Turn) - continue; - if (server.port == 0) - server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478; - - struct addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = - server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM; - hints.ai_protocol = - server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; - hints.ai_flags = AI_ADDRCONFIG; - struct addrinfo *result = nullptr; - if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints, - &result) != 0) { - PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':' - << server.port; - continue; - } - - for (auto p = result; p; p = p->ai_next) { - if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { - char nodebuffer[MAX_NUMERICNODE_LEN]; - char servbuffer[MAX_NUMERICSERV_LEN]; - if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, - servbuffer, MAX_NUMERICSERV_LEN, - NI_NUMERICHOST | NI_NUMERICSERV) == 0) { - PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port - << "\""; - NiceRelayType niceRelayType; - switch (server.relayType) { - case IceServer::RelayType::TurnTcp: - niceRelayType = NICE_RELAY_TYPE_TURN_TCP; - break; - case IceServer::RelayType::TurnTls: - niceRelayType = NICE_RELAY_TYPE_TURN_TLS; - break; - default: - niceRelayType = NICE_RELAY_TYPE_TURN_UDP; - break; - } - nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer, - std::stoul(servbuffer), server.username.c_str(), - server.password.c_str(), niceRelayType); - } - } - } - - freeaddrinfo(result); + addIceServer(server); } g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed", @@ -603,6 +587,60 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi RecvCallback, this); } +void IceTransport::addIceServer(IceServer server) { + if (server.hostname.empty() || server.type != IceServer::Type::Turn) { + PLOG_WARNING << "Only TURN servers are supported as additional ICE servers"; + return; + } + + if (server.port == 0) + server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478; + + struct addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = + server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = + server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; + struct addrinfo *result = nullptr; + if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints, + &result) != 0) { + PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':' + << server.port; + return; + } + + for (auto p = result; p; p = p->ai_next) { + if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { + char nodebuffer[MAX_NUMERICNODE_LEN]; + char servbuffer[MAX_NUMERICSERV_LEN]; + if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, servbuffer, + MAX_NUMERICSERV_LEN, NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port + << "\""; + NiceRelayType niceRelayType; + switch (server.relayType) { + case IceServer::RelayType::TurnTcp: + niceRelayType = NICE_RELAY_TYPE_TURN_TCP; + break; + case IceServer::RelayType::TurnTls: + niceRelayType = NICE_RELAY_TYPE_TURN_TLS; + break; + default: + niceRelayType = NICE_RELAY_TYPE_TURN_UDP; + break; + } + nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer, + std::stoul(servbuffer), server.username.c_str(), + server.password.c_str(), niceRelayType); + } + } + } + + freeaddrinfo(result); +} + IceTransport::~IceTransport() { PLOG_DEBUG << "Destroying ICE transport"; nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(MainLoop.get()), @@ -684,8 +722,13 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { return ret > 0; } -void IceTransport::gatherLocalCandidates(string mid) { +void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); + std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); + + for (auto &server : additionalIceServers) { + addIceServer(server); + } // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); diff --git a/src/impl/icetransport.hpp b/src/impl/icetransport.hpp index 7724e2bda..0dc3c2608 100644 --- a/src/impl/icetransport.hpp +++ b/src/impl/icetransport.hpp @@ -50,7 +50,7 @@ class IceTransport : public Transport { Description getLocalDescription(Description::Type type) const; void setRemoteDescription(const Description &description); bool addRemoteCandidate(const Candidate &candidate); - void gatherLocalCandidates(string mid); + void gatherLocalCandidates(string mid, std::vector additionalIceServers = {}); optional getLocalAddress() const; optional getRemoteAddress() const; @@ -69,6 +69,8 @@ class IceTransport : public Transport { void processGatheringDone(); void processTimeout(); + void addIceServer(IceServer server); + Description::Role mRole; string mMid; std::chrono::milliseconds mTrickleTimeout; @@ -79,6 +81,7 @@ class IceTransport : public Transport { #if !USE_NICE unique_ptr mAgent; + int mTURNServersAdded = 0; static void StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr); static void CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr); diff --git a/src/peerconnection.cpp b/src/peerconnection.cpp index dd1ec7c82..0e146834b 100644 --- a/src/peerconnection.cpp +++ b/src/peerconnection.cpp @@ -146,11 +146,24 @@ void PeerConnection::setLocalDescription(Description::Type type) { impl()->changeSignalingState(newSignalingState); signalingLock.unlock(); - if (impl()->gatheringState == GatheringState::New) { + if (impl()->gatheringState == GatheringState::New && !impl()->config.disableAutoGathering) { iceTransport->gatherLocalCandidates(impl()->localBundleMid()); } } +void PeerConnection::gatherLocalCandidates(std::vector additionalIceServers) { + auto iceTransport = impl()->getIceTransport(); + if (!iceTransport) { + throw std::logic_error("No IceTransport. Local Description has not been set"); + } + + if (impl()->gatheringState == GatheringState::New) { + iceTransport->gatherLocalCandidates(impl()->localBundleMid(), additionalIceServers); + } else { + PLOG_WARNING << "Candidates gathering already started"; + } +} + void PeerConnection::setRemoteDescription(Description description) { std::unique_lock signalingLock(impl()->signalingMutex); PLOG_VERBOSE << "Setting remote description: " << string(description); From f5acd35019a82c6bb5b14de7e56ae0902def3728 Mon Sep 17 00:00:00 2001 From: Robert Edmonds Date: Tue, 19 Mar 2024 01:22:22 -0400 Subject: [PATCH 16/26] H264RtpDepacketizer: De-packetize access units rather than individual NALUs This commit updates the `H264RtpDepacketizer` to accumulate the NALUs for a particular RTP timestamp into a single output message, rather than returning each NALU as an individual message. This helps decoders which may want to see the non-VCL SPS/PPS/etc. NALUs in the same access unit as a VCL NALU rather than as standalone messages. Each NALU in the access unit buffer is prepended with an configurable H.264 start code --- include/rtc/h264rtpdepacketizer.hpp | 7 +- src/h264rtpdepacketizer.cpp | 112 +++++++++++++++------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/include/rtc/h264rtpdepacketizer.hpp b/include/rtc/h264rtpdepacketizer.hpp index 5fe09c916..e4fe9064e 100644 --- a/include/rtc/h264rtpdepacketizer.hpp +++ b/include/rtc/h264rtpdepacketizer.hpp @@ -15,6 +15,7 @@ #include "common.hpp" #include "mediahandler.hpp" #include "message.hpp" +#include "nalunit.hpp" #include "rtp.hpp" #include @@ -24,14 +25,18 @@ namespace rtc { /// RTP depacketization for H264 class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler { public: - H264RtpDepacketizer() = default; + using Separator = NalUnit::Separator; + + H264RtpDepacketizer(Separator separator = Separator::LongStartSequence); virtual ~H264RtpDepacketizer() = default; void incoming(message_vector &messages, const message_callback &send) override; private: std::vector mRtpBuffer; + const NalUnit::Separator mSeparator; + void addSeparator(binary &accessUnit); message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt, uint8_t payloadType, uint32_t timestamp); }; diff --git a/src/h264rtpdepacketizer.cpp b/src/h264rtpdepacketizer.cpp index 200f43e4d..2a03c95bd 100644 --- a/src/h264rtpdepacketizer.cpp +++ b/src/h264rtpdepacketizer.cpp @@ -10,89 +10,93 @@ #include "h264rtpdepacketizer.hpp" #include "nalunit.hpp" -#include "track.hpp" -#include "impl/logcounter.hpp" - -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#endif +#include "impl/internals.hpp" namespace rtc { -const unsigned long stapaHeaderSize = 1; -const auto fuaHeaderSize = 2; +const binary naluLongStartCode = {byte{0}, byte{0}, byte{0}, byte{1}}; +const binary naluShortStartCode = {byte{0}, byte{0}, byte{1}}; const uint8_t naluTypeSTAPA = 24; const uint8_t naluTypeFUA = 28; +H264RtpDepacketizer::H264RtpDepacketizer(Separator separator) : mSeparator(separator) { + if (separator != Separator::StartSequence && separator != Separator::LongStartSequence && + separator != Separator::ShortStartSequence) { + throw std::invalid_argument("Invalid separator"); + } +} + +void H264RtpDepacketizer::addSeparator(binary &accessUnit) { + if (mSeparator == Separator::StartSequence || mSeparator == Separator::LongStartSequence) { + accessUnit.insert(accessUnit.end(), naluLongStartCode.begin(), naluLongStartCode.end()); + } else if (mSeparator == Separator::ShortStartSequence) { + accessUnit.insert(accessUnit.end(), naluShortStartCode.begin(), naluShortStartCode.end()); + } else { + throw std::invalid_argument("Invalid separator"); + } +} + message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, - message_vector::iterator end, uint8_t payloadType, uint32_t timestamp) { + message_vector::iterator end, uint8_t payloadType, + uint32_t timestamp) { message_vector out = {}; - auto fua_buffer = std::vector{}; + auto accessUnit = binary{}; auto frameInfo = std::make_shared(payloadType, timestamp); + auto nFrags = 0; - for (auto it = begin; it != end; it++) { + for (auto it = begin; it != end; ++it) { auto pkt = it->get(); auto pktParsed = reinterpret_cast(pkt->data()); - auto headerSize = - sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize(); - auto paddingSize = 0; + auto rtpHeaderSize = pktParsed->getSize() + pktParsed->getExtensionHeaderSize(); + auto rtpPaddingSize = 0; if (pktParsed->padding()) { - paddingSize = std::to_integer(pkt->at(pkt->size() - 1)); + rtpPaddingSize = std::to_integer(pkt->at(pkt->size() - 1)); } - if (pkt->size() == headerSize + paddingSize) { + if (pkt->size() == rtpHeaderSize + rtpPaddingSize) { PLOG_VERBOSE << "H.264 RTP packet has empty payload"; continue; } - auto nalUnitHeader = NalUnitHeader{std::to_integer(pkt->at(headerSize))}; + auto nalUnitHeader = NalUnitHeader{std::to_integer(pkt->at(rtpHeaderSize))}; - if (fua_buffer.size() != 0 || nalUnitHeader.unitType() == naluTypeFUA) { - if (fua_buffer.size() == 0) { - fua_buffer.push_back(std::byte(0)); - } - - auto nalUnitFragmentHeader = - NalUnitFragmentHeader{std::to_integer(pkt->at(headerSize + 1))}; - - std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(), - std::back_inserter(fua_buffer)); - - if (nalUnitFragmentHeader.isEnd()) { - fua_buffer.at(0) = - std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType()); + if (nalUnitHeader.unitType() == naluTypeFUA) { + auto nalUnitFragmentHeader = NalUnitFragmentHeader{ + std::to_integer(pkt->at(rtpHeaderSize + sizeof(NalUnitHeader)))}; - out.push_back( - make_message(std::move(fua_buffer), Message::Binary, 0, nullptr, frameInfo)); - fua_buffer.clear(); + if (nFrags++ == 0) { + addSeparator(accessUnit); + accessUnit.emplace_back( + byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType())); } + + accessUnit.insert(accessUnit.end(), + pkt->begin() + rtpHeaderSize + sizeof(NalUnitHeader) + + sizeof(NalUnitFragmentHeader), + pkt->end()); } else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) { - out.push_back(make_message(pkt->begin() + headerSize, pkt->end(), Message::Binary, 0, - nullptr, frameInfo)); + addSeparator(accessUnit); + accessUnit.insert(accessUnit.end(), pkt->begin() + rtpHeaderSize, pkt->end()); } else if (nalUnitHeader.unitType() == naluTypeSTAPA) { - auto currOffset = stapaHeaderSize + headerSize; + auto currOffset = rtpHeaderSize + sizeof(NalUnitHeader); - while (currOffset < pkt->size()) { - auto naluSize = - uint16_t(pkt->at(currOffset)) << 8 | uint8_t(pkt->at(currOffset + 1)); + while (currOffset + sizeof(uint16_t) < pkt->size()) { + auto naluSize = std::to_integer(pkt->at(currOffset)) << 8 | + std::to_integer(pkt->at(currOffset + 1)); - currOffset += 2; + currOffset += sizeof(uint16_t); if (pkt->size() < currOffset + naluSize) { - throw std::runtime_error("STAP-A declared size is larger then buffer"); + throw std::runtime_error("H264 STAP-A declared size is larger than buffer"); } - out.push_back(make_message(pkt->begin() + currOffset, - pkt->begin() + currOffset + naluSize, Message::Binary, 0, - nullptr, frameInfo)); + addSeparator(accessUnit); + accessUnit.insert(accessUnit.end(), pkt->begin() + currOffset, + pkt->begin() + currOffset + naluSize); + currOffset += naluSize; } } else { @@ -100,6 +104,11 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, } } + if (!accessUnit.empty()) { + out.emplace_back(make_message(accessUnit.begin(), accessUnit.end(), Message::Binary, 0, + nullptr, frameInfo)); + } + return out; } @@ -131,7 +140,8 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb if (current_timestamp == 0) { current_timestamp = p->timestamp(); - payload_type = p->payloadType(); // should all be the same for data of the same codec + payload_type = + p->payloadType(); // should all be the same for data of the same codec } else if (current_timestamp != p->timestamp()) { break; } From d6e352f1d2150ae5f6ddb5494e2242211bc479cd Mon Sep 17 00:00:00 2001 From: sbarrac <46637437+sbarrac@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:47:35 +0100 Subject: [PATCH 17/26] Update src/rtppacketizer.cpp Co-authored-by: Paul-Louis Ageneau --- src/rtppacketizer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rtppacketizer.cpp b/src/rtppacketizer.cpp index e39c8bb3e..27205184b 100644 --- a/src/rtppacketizer.cpp +++ b/src/rtppacketizer.cpp @@ -96,13 +96,13 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { uint16_t max = rtpConfig->playoutDelayMax & 0xFFF; // 12 bits for min + 12 bits for max - char data[] = { - static_cast((min >> 4) & 0xFF), - static_cast(((min & 0xF) << 4) | ((max >> 8) & 0xF)), - static_cast(max & 0xFF) + byte data[] = { + byte((min >> 4) & 0xFF), + byte(((min & 0xF) << 4) | ((max >> 8) & 0xF)), + byte(max & 0xFF) }; - extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, (byte *)data, 3); + extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, data, 3); offset += 4; } } From c281c46295101fa7e2f30c4d0c07133b90902c82 Mon Sep 17 00:00:00 2001 From: sbarrac <46637437+sbarrac@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:51:58 +0100 Subject: [PATCH 18/26] Changed header size --- src/rtppacketizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtppacketizer.cpp b/src/rtppacketizer.cpp index 27205184b..9cb7332ae 100644 --- a/src/rtppacketizer.cpp +++ b/src/rtppacketizer.cpp @@ -34,7 +34,7 @@ message_ptr RtpPacketizer::packetize(shared_ptr payload, bool mark) { const bool setPlayoutDelay = (rtpConfig->playoutDelayId > 0 && rtpConfig->playoutDelayId < 15); if (setPlayoutDelay) - rtpExtHeaderSize += 1; + rtpExtHeaderSize += 4; if (rtpConfig->mid.has_value()) rtpExtHeaderSize += (1 + rtpConfig->mid->length()); From 56d8de09fd0529996eb91606f3ee469faeab976c Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sun, 7 Apr 2024 00:02:49 -0400 Subject: [PATCH 19/26] Add PacingHandler MediaHandler that can be used to pace packet delivery Resolves #1017 Co-authored-by: Paul-Louis Ageneau --- CMakeLists.txt | 2 + include/rtc/pacinghandler.hpp | 51 ++++++++++++++++++++++++ include/rtc/rtc.hpp | 1 + src/impl/track.cpp | 12 +++++- src/pacinghandler.cpp | 75 +++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 include/rtc/pacinghandler.hpp create mode 100644 src/pacinghandler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cd8f0476a..034697b03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ set(LIBDATACHANNEL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/rtp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/plihandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pacinghandler.cpp ) set(LIBDATACHANNEL_HEADERS @@ -123,6 +124,7 @@ set(LIBDATACHANNEL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpnackresponder.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/utils.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/plihandler.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/pacinghandler.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/version.h ) diff --git a/include/rtc/pacinghandler.hpp b/include/rtc/pacinghandler.hpp new file mode 100644 index 000000000..286a93f6e --- /dev/null +++ b/include/rtc/pacinghandler.hpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2020 Staz Modrzynski + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef RTC_PACING_HANDLER_H +#define RTC_PACING_HANDLER_H + +#if RTC_ENABLE_MEDIA + +#include "mediahandler.hpp" +#include "utils.hpp" + +#include +#include + +namespace rtc { + +// Paced sending of RTP packets. Takes a stream of RTP packets that can an +// uneven bitrate. It then delivers these packets in a smoother manner by +// sending a fixed size of them on an interval +class RTC_CPP_EXPORT PacingHandler : public MediaHandler { +public: + PacingHandler(double bitsPerSecond, std::chrono::milliseconds sendInterval); + + void outgoing(message_vector &messages, const message_callback &send) override; + +private: + std::atomic mHaveScheduled = false; + + double mBytesPerSecond; + double mBudget; + + std::chrono::milliseconds mSendInterval; + std::chrono::time_point mLastRun; + + std::mutex mMutex; + std::queue mRtpBuffer; + + void schedule(const message_callback &send); +}; + +} // namespace rtc + +#endif // RTC_ENABLE_MEDIA + +#endif // RTC_PACING_HANDLER_H diff --git a/include/rtc/rtc.hpp b/include/rtc/rtc.hpp index 683219fc9..2f3f46b59 100644 --- a/include/rtc/rtc.hpp +++ b/include/rtc/rtc.hpp @@ -34,6 +34,7 @@ #include "h265rtppacketizer.hpp" #include "mediahandler.hpp" #include "plihandler.hpp" +#include "pacinghandler.hpp" #include "rtcpnackresponder.hpp" #include "rtcpreceivingsession.hpp" #include "rtcpsrreporter.hpp" diff --git a/src/impl/track.cpp b/src/impl/track.cpp index bb2d29557..588045755 100644 --- a/src/impl/track.cpp +++ b/src/impl/track.cpp @@ -142,7 +142,11 @@ void Track::incoming(message_ptr message) { message_vector messages{std::move(message)}; if (auto handler = getMediaHandler()) - handler->incomingChain(messages, [this](message_ptr m) { transportSend(m); }); + handler->incomingChain(messages, [this, weak_this = weak_from_this()](message_ptr m) { + if (auto locked = weak_this.lock()) { + transportSend(m); + } + }); for (auto &m : messages) { // Tail drop if queue is full @@ -175,7 +179,11 @@ bool Track::outgoing(message_ptr message) { if (handler) { message_vector messages{std::move(message)}; - handler->outgoingChain(messages, [this](message_ptr m) { transportSend(m); }); + handler->outgoingChain(messages, [this, weak_this = weak_from_this()](message_ptr m) { + if (auto locked = weak_this.lock()) { + transportSend(m); + } + }); bool ret = false; for (auto &m : messages) ret = transportSend(std::move(m)); diff --git a/src/pacinghandler.cpp b/src/pacinghandler.cpp new file mode 100644 index 000000000..2dccadc01 --- /dev/null +++ b/src/pacinghandler.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2020 Filip Klembara (in2core) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#if RTC_ENABLE_MEDIA + +#include + +#include "pacinghandler.hpp" + +#include "impl/internals.hpp" +#include "impl/threadpool.hpp" + +namespace rtc { + +PacingHandler::PacingHandler(double bitsPerSecond, std::chrono::milliseconds sendInterval) + : mBytesPerSecond(bitsPerSecond / 8), mBudget(0), mSendInterval(sendInterval){}; + +void PacingHandler::schedule(const message_callback &send) { + if (!mHaveScheduled.exchange(true)) { + return; + } + + impl::ThreadPool::Instance().schedule(mSendInterval, [this, weak_this = weak_from_this(), + send]() { + if (auto locked = weak_this.lock()) { + const std::lock_guard lock(mMutex); + mHaveScheduled.store(false); + + // Update the budget and cap it + auto newBudget = + std::chrono::duration(std::chrono::high_resolution_clock::now() - mLastRun) + .count() * + mBytesPerSecond; + auto maxBudget = std::chrono::duration(mSendInterval).count() * mBytesPerSecond; + mBudget = std::min(mBudget + newBudget, maxBudget); + mLastRun = std::chrono::high_resolution_clock::now(); + + // Send packets while there is budget, allow a single partial packet over budget + while (!mRtpBuffer.empty() && mBudget > 0) { + auto size = int(mRtpBuffer.front()->size()); + send(std::move(mRtpBuffer.front())); + mRtpBuffer.pop(); + mBudget -= size; + } + + if (!mRtpBuffer.empty()) { + printf("\n more \n"); + schedule(send); + } else { + printf("\n empty \n"); + } + } + }); +} + +void PacingHandler::outgoing(message_vector &messages, const message_callback &send) { + + std::lock_guard lock(mMutex); + + for (auto &m : messages) { + mRtpBuffer.push(std::move(m)); + } + messages.clear(); + + schedule(send); +} + +} // namespace rtc + +#endif /* RTC_ENABLE_MEDIA */ From 1ab7d869e79ffdd156c70682a481334eb7a2b6aa Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Fri, 26 Apr 2024 22:26:42 +0200 Subject: [PATCH 20/26] Remove debug printf --- src/pacinghandler.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pacinghandler.cpp b/src/pacinghandler.cpp index 2dccadc01..f1f1cbbd0 100644 --- a/src/pacinghandler.cpp +++ b/src/pacinghandler.cpp @@ -49,10 +49,7 @@ void PacingHandler::schedule(const message_callback &send) { } if (!mRtpBuffer.empty()) { - printf("\n more \n"); schedule(send); - } else { - printf("\n empty \n"); } } }); From 40dcebc274d39a39e7c33ae6cb3c57c8033975f6 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Fri, 26 Apr 2024 22:30:32 +0200 Subject: [PATCH 21/26] Update libjuice to v1.4.1 --- deps/libjuice | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libjuice b/deps/libjuice index ac0fc81d4..502da0674 160000 --- a/deps/libjuice +++ b/deps/libjuice @@ -1 +1 @@ -Subproject commit ac0fc81d41be19d58195d6a0fc28c4c5b67a1626 +Subproject commit 502da067489073ecaef892e6639d0adf63ba1ac6 From f63702c669db5f2fd276ef74d8ef1e0b35125d59 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Fri, 26 Apr 2024 22:40:08 +0200 Subject: [PATCH 22/26] Fix copyright in pacinghandler.hpp --- include/rtc/pacinghandler.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/rtc/pacinghandler.hpp b/include/rtc/pacinghandler.hpp index 286a93f6e..5e6ace6e3 100644 --- a/include/rtc/pacinghandler.hpp +++ b/include/rtc/pacinghandler.hpp @@ -1,6 +1,5 @@ /** - * Copyright (c) 2020 Staz Modrzynski - * Copyright (c) 2020 Paul-Louis Ageneau + * Copyright (c) 2024 Sean DuBois * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this From 118a692e41d58773ded9426606bec1bd9e9649a0 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Fri, 26 Apr 2024 22:41:02 +0200 Subject: [PATCH 23/26] Fix copyright in pacinghandler.cpp --- src/pacinghandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pacinghandler.cpp b/src/pacinghandler.cpp index f1f1cbbd0..c8c447292 100644 --- a/src/pacinghandler.cpp +++ b/src/pacinghandler.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020 Filip Klembara (in2core) + * Copyright (c) 2024 Sean DuBois * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this From fa538f76849c3ef3803972f676ea56d8f394877e Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Fri, 26 Apr 2024 23:53:24 +0200 Subject: [PATCH 24/26] Cleanup --- include/rtc/rtp.hpp | 19 ----------- src/h264rtpdepacketizer.cpp | 4 +-- src/impl/icetransport.cpp | 64 ++++++++++++++----------------------- src/impl/icetransport.hpp | 2 +- 4 files changed, 27 insertions(+), 62 deletions(-) diff --git a/include/rtc/rtp.hpp b/include/rtc/rtp.hpp index 503a61ff5..7eb89e516 100644 --- a/include/rtc/rtp.hpp +++ b/include/rtc/rtp.hpp @@ -354,25 +354,6 @@ struct RTC_CPP_EXPORT RtpRtx { size_t copyTo(RtpHeader *dest, size_t totalSize, uint8_t originalPayloadType); }; -// For backward compatibility, do not use -using RTP_ExtensionHeader [[deprecated]] = RtpExtensionHeader; -using RTP [[deprecated]] = RtpHeader; -using RTCP_ReportBlock [[deprecated]] = RtcpReportBlock; -using RTCP_HEADER [[deprecated]] = RtcpHeader; -using RTCP_FB_HEADER [[deprecated]] = RtcpFbHeader; -using RTCP_SR [[deprecated]] = RtcpSr; -using RTCP_SDES_ITEM [[deprecated]] = RtcpSdesItem; -using RTCP_SDES_CHUNK [[deprecated]] = RtcpSdesChunk; -using RTCP_SDES [[deprecated]] = RtcpSdes; -using RTCP_RR [[deprecated]] = RtcpRr; -using RTCP_REMB [[deprecated]] = RtcpRemb; -using RTCP_PLI [[deprecated]] = RtcpPli; -using RTCP_FIR_PART [[deprecated]] = RtcpFirPart; -using RTCP_FIR [[deprecated]] = RtcpFir; -using RTCP_NACK_PART [[deprecated]] = RtcpNackPart; -using RTCP_NACK [[deprecated]] = RtcpNack; -using RTP_RTX [[deprecated]] = RtpRtx; - #pragma pack(pop) } // namespace rtc diff --git a/src/h264rtpdepacketizer.cpp b/src/h264rtpdepacketizer.cpp index 2a03c95bd..f2ddabb4c 100644 --- a/src/h264rtpdepacketizer.cpp +++ b/src/h264rtpdepacketizer.cpp @@ -105,8 +105,8 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin, } if (!accessUnit.empty()) { - out.emplace_back(make_message(accessUnit.begin(), accessUnit.end(), Message::Binary, 0, - nullptr, frameInfo)); + out.emplace_back( + make_message(std::move(accessUnit), Message::Binary, 0, nullptr, frameInfo)); } return out; diff --git a/src/impl/icetransport.cpp b/src/impl/icetransport.cpp index a2c56b0aa..a27a2ed32 100644 --- a/src/impl/icetransport.cpp +++ b/src/impl/icetransport.cpp @@ -134,30 +134,24 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi if (!mAgent) throw std::runtime_error("Failed to create the ICE agent"); - // Filter STUN servers - servers.erase(std::remove_if(servers.begin(), servers.end(), - [](const IceServer &server) { - return server.hostname.empty() || - server.type != IceServer::Type::Turn; - }), - servers.end()); - // Add TURN servers - for (auto &server : servers) { - if (mTURNServersAdded++ >= MAX_TURN_SERVERS_COUNT) { - break; - } - - addIceServer(server); - } + for (const auto &server : servers) + if (!server.hostname.empty() && server.type != IceServer::Type::Stun) + addIceServer(server); } void IceTransport::addIceServer(IceServer server) { - if (server.hostname.empty() || server.type != IceServer::Type::Turn) { + if (server.hostname.empty()) + return; + + if (server.type != IceServer::Type::Turn) { PLOG_WARNING << "Only TURN servers are supported as additional ICE servers"; return; } + if (mTurnServersAdded >= MAX_TURN_SERVERS_COUNT) + return; + if (server.port == 0) server.port = 3478; // TURN UDP port @@ -168,9 +162,10 @@ void IceTransport::addIceServer(IceServer server) { turn_server.password = server.password.c_str(); turn_server.port = server.port; - if (juice_add_turn_server(mAgent.get(), &turn_server) != 0) { + if (juice_add_turn_server(mAgent.get(), &turn_server) != 0) throw std::runtime_error("Failed to add TURN server"); - } + + ++mTurnServersAdded; } IceTransport::~IceTransport() { @@ -229,15 +224,10 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); - std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); - - for (auto &server : additionalIceServers) { - if (mTURNServersAdded++ >= MAX_TURN_SERVERS_COUNT) { - break; - } + std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); + for (const auto &server : additionalIceServers) addIceServer(server); - } // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); @@ -559,18 +549,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi break; } - // Filter STUN servers - servers.erase(std::remove_if(servers.begin(), servers.end(), - [](const IceServer &server) { - return server.hostname.empty() || - server.type != IceServer::Type::Turn; - }), - servers.end()); - // Add TURN servers - for (auto &server : servers) { - addIceServer(server); - } + for (const auto &server : servers) + if (!server.hostname.empty() && server.type != IceServer::Type::Stun) + addIceServer(server); g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed", G_CALLBACK(StateChangeCallback), this); @@ -588,7 +570,10 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi } void IceTransport::addIceServer(IceServer server) { - if (server.hostname.empty() || server.type != IceServer::Type::Turn) { + if (server.hostname.empty()) + return; + + if (server.type != IceServer::Type::Turn) { PLOG_WARNING << "Only TURN servers are supported as additional ICE servers"; return; } @@ -724,11 +709,10 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); - std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); - for (auto &server : additionalIceServers) { + std::shuffle(additionalIceServers.begin(), additionalIceServers.end(), utils::random_engine()); + for (const auto &server : additionalIceServers) addIceServer(server); - } // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); diff --git a/src/impl/icetransport.hpp b/src/impl/icetransport.hpp index 0dc3c2608..4133bcc2f 100644 --- a/src/impl/icetransport.hpp +++ b/src/impl/icetransport.hpp @@ -81,7 +81,7 @@ class IceTransport : public Transport { #if !USE_NICE unique_ptr mAgent; - int mTURNServersAdded = 0; + int mTurnServersAdded = 0; static void StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr); static void CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr); From 44f5fd8910cfe6c0493ec4f4d2762a189db7451f Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Sat, 27 Apr 2024 00:18:36 +0200 Subject: [PATCH 25/26] Fix comment --- include/rtc/pacinghandler.hpp | 5 ++--- src/pacinghandler.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/rtc/pacinghandler.hpp b/include/rtc/pacinghandler.hpp index 5e6ace6e3..c1e216ad8 100644 --- a/include/rtc/pacinghandler.hpp +++ b/include/rtc/pacinghandler.hpp @@ -19,9 +19,8 @@ namespace rtc { -// Paced sending of RTP packets. Takes a stream of RTP packets that can an -// uneven bitrate. It then delivers these packets in a smoother manner by -// sending a fixed size of them on an interval +// Paced sending of RTP packets. It takes a stream of RTP packets that can have an uneven bitrate +// and delivers them in a smoother manner by sending a fixed size of them on an interval class RTC_CPP_EXPORT PacingHandler : public MediaHandler { public: PacingHandler(double bitsPerSecond, std::chrono::milliseconds sendInterval); diff --git a/src/pacinghandler.cpp b/src/pacinghandler.cpp index c8c447292..8377f372c 100644 --- a/src/pacinghandler.cpp +++ b/src/pacinghandler.cpp @@ -18,7 +18,7 @@ namespace rtc { PacingHandler::PacingHandler(double bitsPerSecond, std::chrono::milliseconds sendInterval) - : mBytesPerSecond(bitsPerSecond / 8), mBudget(0), mSendInterval(sendInterval){}; + : mBytesPerSecond(bitsPerSecond / 8), mBudget(0.), mSendInterval(sendInterval){}; void PacingHandler::schedule(const message_callback &send) { if (!mHaveScheduled.exchange(true)) { From 9d5c46b8f506943727104d766e5dad0693c5a223 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Sat, 27 Apr 2024 00:21:20 +0200 Subject: [PATCH 26/26] Bumped version to 0.21.0 --- CMakeLists.txt | 2 +- include/rtc/version.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 953d8b145..9f1158b6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.7) project(libdatachannel - VERSION 0.20.3 + VERSION 0.21.0 LANGUAGES CXX) set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets") diff --git a/include/rtc/version.h b/include/rtc/version.h index ff74b435b..6d0535052 100644 --- a/include/rtc/version.h +++ b/include/rtc/version.h @@ -2,8 +2,8 @@ #define RTC_VERSION_H #define RTC_VERSION_MAJOR 0 -#define RTC_VERSION_MINOR 20 -#define RTC_VERSION_PATCH 3 -#define RTC_VERSION "0.20.3" +#define RTC_VERSION_MINOR 21 +#define RTC_VERSION_PATCH 0 +#define RTC_VERSION "0.21.0" #endif