From 4423d6942627fd8458e488333b1f059ebe2d243c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20R=C3=A4s=C3=A4nen?= Date: Wed, 12 Apr 2023 12:23:10 +0300 Subject: [PATCH 1/7] build: Set CMake build dir so that uvgRTP can be built fetchcontent --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 334ca85..8165197 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,7 @@ target_link_libraries(${PROJECT_NAME} target_include_directories(${PROJECT_NAME} PUBLIC $ - PRIVATE $ + PRIVATE $ PUBLIC $ ) From d1e91df7c2bf1bde68b00c734ab44cd216b65765 Mon Sep 17 00:00:00 2001 From: "william.fink" Date: Thu, 20 Apr 2023 11:52:22 +0200 Subject: [PATCH 2/7] common: Added multicast bind support for ipv4 and ipv6 --- src/socket.cc | 98 +++++++++++++++++++++++++++++++++++++++----- test/test_2_rtp.cpp | 33 ++++++++++++++- test/test_7_ipv6.cpp | 34 ++++++++++++++- 3 files changed, 153 insertions(+), 12 deletions(-) diff --git a/src/socket.cc b/src/socket.cc index 5ed4261..e04d37a 100644 --- a/src/socket.cc +++ b/src/socket.cc @@ -108,38 +108,116 @@ rtp_error_t uvgrtp::socket::bind(short family, unsigned host, short port) return bind(local_address_); } +static bool is_multicast(sockaddr_in& local_address) +{ + // Multicast addresses ranges from 224.0.0.0 to 239.255.255.255 (0xE0000000 to 0xEFFFFFFF) + auto addr = local_address.sin_addr.s_addr; + return (ntohl(addr) & 0xF0000000) == 0xE0000000; +} + rtp_error_t uvgrtp::socket::bind(sockaddr_in& local_address) { local_address_ = local_address; UVG_LOG_DEBUG("Binding to address %s", sockaddr_to_string(local_address_).c_str()); - if (::bind(socket_, (struct sockaddr*)&local_address_, sizeof(local_address_)) < 0) { + if (!::is_multicast(local_address_)) { + // Regular address + if (::bind(socket_, (struct sockaddr*)&local_address_, sizeof(local_address_)) < 0) { #ifdef _WIN32 - win_get_last_error(); + win_get_last_error(); #else - fprintf(stderr, "%s\n", strerror(errno)); + fprintf(stderr, "%s\n", strerror(errno)); #endif - UVG_LOG_ERROR("Binding to port %u failed!", ntohs(local_address_.sin_port)); - return RTP_BIND_ERROR; + UVG_LOG_ERROR("Binding to port %u failed!", ntohs(local_address_.sin_port)); + return RTP_BIND_ERROR; + } + } else { + // Multicast address + // Bind with empty address + auto bind_addr_in = local_address_; + bind_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + + if (::bind(socket_, (struct sockaddr*)&bind_addr_in, sizeof(bind_addr_in)) < 0) { +#ifdef _WIN32 + win_get_last_error(); +#else + fprintf(stderr, "%s\n", strerror(errno)); +#endif + UVG_LOG_ERROR("Binding to port %u failed!", ntohs(bind_addr_in.sin_port)); + return RTP_BIND_ERROR; + } + + // Join multicast membership + struct ip_mreq mreq{}; + mreq.imr_multiaddr.s_addr = local_address_.sin_addr.s_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + if (::setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) < 0) { +#ifdef _WIN32 + win_get_last_error(); +#else + fprintf(stderr, "%s\n", strerror(errno)); +#endif + UVG_LOG_ERROR("Multicast join failed!"); + return RTP_BIND_ERROR; + } } return RTP_OK; } +static bool is_multicast(sockaddr_in6& local_address) +{ + // Multicast IP addresses have their first byte equals to 0xFF + auto addr = local_address.sin6_addr.s6_addr; + return addr[0] == 0xFF; +} + rtp_error_t uvgrtp::socket::bind_ip6(sockaddr_in6& local_address) { local_ip6_address_ = local_address; UVG_LOG_DEBUG("Binding to address %s", sockaddr_ip6_to_string(local_ip6_address_).c_str()); - if (::bind(socket_, (struct sockaddr*)&local_ip6_address_, sizeof(local_ip6_address_)) < 0) { + if (!::is_multicast(local_ip6_address_)) { + if (::bind(socket_, (struct sockaddr*)&local_ip6_address_, sizeof(local_ip6_address_)) < 0) { + #ifdef _WIN32 + win_get_last_error(); + #else + fprintf(stderr, "%s\n", strerror(errno)); + #endif + UVG_LOG_ERROR("Binding to port %u failed!", ntohs(local_ip6_address_.sin6_port)); + return RTP_BIND_ERROR; + } + } else { + // Multicast address + // Bind with empty address + auto bind_addr_in = local_ip6_address_; + bind_addr_in.sin6_addr = in6addr_any; + + if (::bind(socket_, (struct sockaddr*)&bind_addr_in, sizeof(bind_addr_in)) < 0) { #ifdef _WIN32 - win_get_last_error(); + win_get_last_error(); #else - fprintf(stderr, "%s\n", strerror(errno)); + fprintf(stderr, "%s\n", strerror(errno)); #endif - UVG_LOG_ERROR("Binding to port %u failed!", ntohs(local_address_.sin_port)); - return RTP_BIND_ERROR; + UVG_LOG_ERROR("Binding to port %u failed!", ntohs(bind_addr_in.sin6_port)); + return RTP_BIND_ERROR; + } + + // Join multicast membership + struct ipv6_mreq mreq{}; + memcpy(&mreq.ipv6mr_multiaddr, &local_ip6_address_.sin6_addr, sizeof(mreq.ipv6mr_multiaddr)); + + if (::setsockopt(socket_, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&mreq, sizeof(mreq)) < 0) { +#ifdef _WIN32 + win_get_last_error(); +#else + fprintf(stderr, "%s\n", strerror(errno)); +#endif + UVG_LOG_ERROR("Multicast join failed!"); + return RTP_BIND_ERROR; + } } return RTP_OK; diff --git a/test/test_2_rtp.cpp b/test/test_2_rtp.cpp index 98a0195..1e3c6a4 100644 --- a/test/test_2_rtp.cpp +++ b/test/test_2_rtp.cpp @@ -8,6 +8,7 @@ constexpr uint16_t SEND_PORT = 9300; constexpr char REMOTE_ADDRESS[] = "127.0.0.1"; +constexpr char MULTICAST_ADDRESS[] = "224.0.0.122"; constexpr uint16_t RECEIVE_PORT = 9302; void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); @@ -384,4 +385,34 @@ TEST(RTPTests, send_large_amounts) cleanup_ms(sess, sender); cleanup_ms(sess, receiver); cleanup_sess(ctx, sess); -} \ No newline at end of file +} + +TEST(RTPTests, rtp_multicast) +{ + // Tests with a multicast address + std::cout << "Starting RTP multicast test" << std::endl; + uvgrtp::context ctx; + uvgrtp::session* sess = ctx.create_session(MULTICAST_ADDRESS, MULTICAST_ADDRESS); + + uvgrtp::media_stream* sender = nullptr; + uvgrtp::media_stream* receiver = nullptr; + + int flags = RCE_FRAGMENT_GENERIC; + if (sess) + { + sender = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_GENERIC, flags); + receiver = sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags); + } + + int test_packets = 10; + std::vector sizes = { 1000, 2000 }; + for (size_t& size : sizes) + { + std::unique_ptr test_frame = create_test_packet(RTP_FORMAT_GENERIC, 0, false, size, RTP_NO_FLAGS); + test_packet_size(std::move(test_frame), test_packets, size, sess, sender, receiver, RTP_NO_FLAGS); + } + + cleanup_ms(sess, sender); + cleanup_ms(sess, receiver); + cleanup_sess(ctx, sess); +} diff --git a/test/test_7_ipv6.cpp b/test/test_7_ipv6.cpp index a81a9f9..4d4b032 100644 --- a/test/test_7_ipv6.cpp +++ b/test/test_7_ipv6.cpp @@ -9,6 +9,8 @@ constexpr uint16_t SEND_PORT = 9300; constexpr char REMOTE_ADDRESS[] = "::1"; constexpr uint16_t RECEIVE_PORT = 9400; +constexpr char MULTICAST_INTERFACE[] = "FF02:0:0:0:0:0:0:0"; + // RTCP TEST PARAMETERS constexpr uint16_t PAYLOAD_LEN = 256; constexpr uint16_t FRAME_RATE = 30; @@ -528,4 +530,34 @@ void zrtp_receive_func6(uvgrtp::session* receiver_session, int sender_port, int } cleanup_ms(receiver_session, recv); -} \ No newline at end of file +} + +TEST(RTPTests_ip6, rtp_multicast_ip6) +{ + // Tests installing a hook to uvgRTP + std::cout << "Starting IPv6 RTP hook test" << std::endl; + uvgrtp::context ctx; + uvgrtp::session* sess = ctx.create_session(MULTICAST_INTERFACE, MULTICAST_INTERFACE); + + uvgrtp::media_stream* sender = nullptr; + uvgrtp::media_stream* receiver = nullptr; + + int flags = RCE_FRAGMENT_GENERIC; + if (sess) + { + sender = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_GENERIC, flags); + receiver = sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags); + } + + int test_packets = 10; + std::vector sizes = { 1000, 2000 }; + for (size_t& size : sizes) + { + std::unique_ptr test_frame = create_test_packet(RTP_FORMAT_GENERIC, 0, false, size, RTP_NO_FLAGS); + test_packet_size(std::move(test_frame), test_packets, size, sess, sender, receiver, RTP_NO_FLAGS); + } + + cleanup_ms(sess, sender); + cleanup_ms(sess, receiver); + cleanup_sess(ctx, sess); +} From 6f09cdb0fcd014517f8fa95aaeb9ea0ee58b3960 Mon Sep 17 00:00:00 2001 From: "william.fink" Date: Fri, 21 Apr 2023 15:24:04 +0200 Subject: [PATCH 3/7] common: Enable receiving a multicast stream multiple times --- src/socket.cc | 24 ++++++++++++++++++++++++ test/test_2_rtp.cpp | 31 +++++++++++++++++++++++++++++++ test/test_7_ipv6.cpp | 35 +++++++++++++++++++++++++++++++++-- test/test_common.hh | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/socket.cc b/src/socket.cc index e04d37a..bf4b535 100644 --- a/src/socket.cc +++ b/src/socket.cc @@ -134,6 +134,18 @@ rtp_error_t uvgrtp::socket::bind(sockaddr_in& local_address) } } else { // Multicast address + // Reuse address to enabled receiving the same stream multiple times + const int enable = 1; + if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0) { +#ifdef _WIN32 + win_get_last_error(); +#else + fprintf(stderr, "%s\n", strerror(errno)); +#endif + + UVG_LOG_ERROR("Reuse address failed!"); + } + // Bind with empty address auto bind_addr_in = local_address_; bind_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); @@ -191,6 +203,18 @@ rtp_error_t uvgrtp::socket::bind_ip6(sockaddr_in6& local_address) } } else { // Multicast address + // Reuse address to enabled receiving the same stream multiple times + const int enable = 1; + if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0) { +#ifdef _WIN32 + win_get_last_error(); +#else + fprintf(stderr, "%s\n", strerror(errno)); +#endif + + UVG_LOG_ERROR("Reuse address failed!"); + } + // Bind with empty address auto bind_addr_in = local_ip6_address_; bind_addr_in.sin6_addr = in6addr_any; diff --git a/test/test_2_rtp.cpp b/test/test_2_rtp.cpp index 1e3c6a4..20b1bad 100644 --- a/test/test_2_rtp.cpp +++ b/test/test_2_rtp.cpp @@ -416,3 +416,34 @@ TEST(RTPTests, rtp_multicast) cleanup_ms(sess, receiver); cleanup_sess(ctx, sess); } + +TEST(RTPTests, rtp_multicast_multiple) +{ + // Tests with a multicast address + std::cout << "Starting RTP multicast test" << std::endl; + uvgrtp::context ctx; + uvgrtp::session* sess = ctx.create_session(MULTICAST_ADDRESS, MULTICAST_ADDRESS); + + EXPECT_NE(nullptr, sess); + if (!sess) return; + + int flags = RCE_FRAGMENT_GENERIC; + + auto sender = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_GENERIC, flags); + std::vector receivers = { + sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags), + sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags) + }; + + int test_packets = 10; + std::vector sizes = { 1000, 2000 }; + for (size_t& size : sizes) + { + std::unique_ptr test_frame = create_test_packet(RTP_FORMAT_GENERIC, 0, false, size, RTP_NO_FLAGS); + test_packet_size(std::move(test_frame), test_packets, size, sess, sender, receivers, RTP_NO_FLAGS); + } + + cleanup_ms(sess, sender); + for (auto receiver: receivers) cleanup_ms(sess, receiver); + cleanup_sess(ctx, sess); +} diff --git a/test/test_7_ipv6.cpp b/test/test_7_ipv6.cpp index 4d4b032..7ccf259 100644 --- a/test/test_7_ipv6.cpp +++ b/test/test_7_ipv6.cpp @@ -9,7 +9,7 @@ constexpr uint16_t SEND_PORT = 9300; constexpr char REMOTE_ADDRESS[] = "::1"; constexpr uint16_t RECEIVE_PORT = 9400; -constexpr char MULTICAST_INTERFACE[] = "FF02:0:0:0:0:0:0:0"; +constexpr char MULTICAST_ADDRESS[] = "FF02:0:0:0:0:0:0:0"; // RTCP TEST PARAMETERS constexpr uint16_t PAYLOAD_LEN = 256; @@ -537,7 +537,7 @@ TEST(RTPTests_ip6, rtp_multicast_ip6) // Tests installing a hook to uvgRTP std::cout << "Starting IPv6 RTP hook test" << std::endl; uvgrtp::context ctx; - uvgrtp::session* sess = ctx.create_session(MULTICAST_INTERFACE, MULTICAST_INTERFACE); + uvgrtp::session* sess = ctx.create_session(MULTICAST_ADDRESS, MULTICAST_ADDRESS); uvgrtp::media_stream* sender = nullptr; uvgrtp::media_stream* receiver = nullptr; @@ -561,3 +561,34 @@ TEST(RTPTests_ip6, rtp_multicast_ip6) cleanup_ms(sess, receiver); cleanup_sess(ctx, sess); } + +TEST(RTPTests_ip6, rtp_multicast_multiple_ip6) +{ + // Tests with a multicast address + std::cout << "Starting RTP multicast test" << std::endl; + uvgrtp::context ctx; + uvgrtp::session* sess = ctx.create_session(MULTICAST_ADDRESS, MULTICAST_ADDRESS); + + EXPECT_NE(nullptr, sess); + if (!sess) return; + + int flags = RCE_FRAGMENT_GENERIC; + + auto sender = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_GENERIC, flags); + std::vector receivers = { + sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags), + sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_GENERIC, flags) + }; + + int test_packets = 10; + std::vector sizes = { 1000, 2000 }; + for (size_t& size : sizes) + { + std::unique_ptr test_frame = create_test_packet(RTP_FORMAT_GENERIC, 0, false, size, RTP_NO_FLAGS); + test_packet_size(std::move(test_frame), test_packets, size, sess, sender, receivers, RTP_NO_FLAGS); + } + + cleanup_ms(sess, sender); + for (auto receiver : receivers) cleanup_ms(sess, receiver); + cleanup_sess(ctx, sess); +} diff --git a/test/test_common.hh b/test/test_common.hh index 6f3d814..2a31990 100644 --- a/test/test_common.hh +++ b/test/test_common.hh @@ -14,6 +14,11 @@ inline std::unique_ptr create_test_packet(rtp_format_t format, uint8_ inline void test_packet_size(std::unique_ptr test_packet, int packets, size_t size, uvgrtp::session* sess, uvgrtp::media_stream* sender, uvgrtp::media_stream* receiver, int rtp_flags, int framerate = 25); +inline void test_packet_size(std::unique_ptr test_packet, int packets, size_t size, + uvgrtp::session* sess, + uvgrtp::media_stream* sender, std::vector const& receiver, + int rtp_flags, int framerate = 25); + inline void send_packets(std::unique_ptr test_packet, size_t size, uvgrtp::session* sess, uvgrtp::media_stream* sender, int packets, int packet_interval_ms, bool print_progress, int rtp_flags, bool send_app = false); @@ -208,6 +213,41 @@ inline void test_packet_size(std::unique_ptr test_packet, int packets } } +inline void test_packet_size(std::unique_ptr test_packet, int packets, size_t size, + uvgrtp::session* sess, uvgrtp::media_stream* sender, + std::vector const& receivers, + int rtp_flags, int framerate) +{ + EXPECT_NE(nullptr, sess); + EXPECT_NE(nullptr, sender); + + if (sess && sender) + { + std::vector testers(receivers.size(), { packets }); + + int interval_ms = 1000 / framerate; + + for (auto i = 0; i < receivers.size(); ++i) { + auto receiver = receivers[i]; + + EXPECT_NE(nullptr, receiver); + + if (!receiver) return; + + add_hook(&testers[i], receiver, rtp_receive_hook); + } + + // to increase the likelyhood that receiver thread is ready to receive + std::this_thread::sleep_for(std::chrono::milliseconds(25)); + + send_packets(std::move(test_packet), size, sess, sender, packets, interval_ms, false, rtp_flags); + + std::this_thread::sleep_for(std::chrono::milliseconds(50 + size / 500)); + + for (auto& tester : testers) tester.gotAll(); + } +} + inline void add_hook(Test_receiver* tester, uvgrtp::media_stream* receiver, void (*hook)(void*, uvgrtp::frame::rtp_frame*)) { From 62839d84a83d521e11db3a30703000407cfab0df Mon Sep 17 00:00:00 2001 From: Heikki Tampio Date: Wed, 19 Apr 2023 12:25:22 +0300 Subject: [PATCH 4/7] rtcp: Fix compiler errors when using C++14 --- src/rtcp.cc | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/rtcp.cc b/src/rtcp.cc index c73e81c..0b997b4 100644 --- a/src/rtcp.cc +++ b/src/rtcp.cc @@ -757,7 +757,7 @@ rtp_error_t uvgrtp::rtcp::install_app_hook(std::function prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { return nullptr; @@ -773,7 +773,7 @@ uvgrtp::frame::rtcp_sender_report* uvgrtp::rtcp::get_sender_packet(uint32_t ssrc uvgrtp::frame::rtcp_receiver_report* uvgrtp::rtcp::get_receiver_packet(uint32_t ssrc) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { return nullptr; @@ -789,7 +789,7 @@ uvgrtp::frame::rtcp_receiver_report* uvgrtp::rtcp::get_receiver_packet(uint32_t uvgrtp::frame::rtcp_sdes_packet* uvgrtp::rtcp::get_sdes_packet(uint32_t ssrc) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { return nullptr; @@ -805,7 +805,7 @@ uvgrtp::frame::rtcp_sdes_packet* uvgrtp::rtcp::get_sdes_packet(uint32_t ssrc) uvgrtp::frame::rtcp_app_packet* uvgrtp::rtcp::get_app_packet(uint32_t ssrc) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { return nullptr; @@ -826,7 +826,7 @@ std::vector uvgrtp::rtcp::get_participants() const for (auto& i : participants_) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); ssrcs.push_back(i.first); } @@ -883,7 +883,7 @@ void uvgrtp::rtcp::zero_stats(uvgrtp::receiver_statistics *stats) bool uvgrtp::rtcp::is_participant(uint32_t ssrc) const { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); return participants_.find(ssrc) != participants_.end(); } @@ -977,7 +977,7 @@ rtp_error_t uvgrtp::rtcp::update_sender_stats(size_t pkt_size) rtp_error_t uvgrtp::rtcp::init_participant_seq(uint32_t ssrc, uint16_t base_seq) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { return RTP_NOT_FOUND; @@ -992,7 +992,7 @@ rtp_error_t uvgrtp::rtcp::init_participant_seq(uint32_t ssrc, uint16_t base_seq) rtp_error_t uvgrtp::rtcp::update_participant_seq(uint32_t ssrc, uint16_t seq) { - std::unique_lock prtcp_lock(participants_mutex_); + std::unique_lock prtcp_lock(participants_mutex_); if (participants_.find(ssrc) == participants_.end()) { UVG_LOG_ERROR("Did not find participant SSRC when updating seq"); @@ -1054,7 +1054,7 @@ rtp_error_t uvgrtp::rtcp::update_participant_seq(uint32_t ssrc, uint16_t seq) rtp_error_t uvgrtp::rtcp::reset_rtcp_state(uint32_t ssrc) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_.find(ssrc) != participants_.end()) { return RTP_SSRC_COLLISION; @@ -1067,13 +1067,13 @@ rtp_error_t uvgrtp::rtcp::reset_rtcp_state(uint32_t ssrc) bool uvgrtp::rtcp::collision_detected(uint32_t ssrc) const { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); return participants_.find(ssrc) == participants_.end(); } void uvgrtp::rtcp::update_session_statistics(const uvgrtp::frame::rtp_frame *frame) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); participants_[frame->header.ssrc]->stats.received_rtp_packet = true; participants_[frame->header.ssrc]->stats.received_pkts += 1; @@ -1406,7 +1406,7 @@ rtp_error_t uvgrtp::rtcp::handle_receiver_report_packet(uint8_t* buffer, size_t& rr_hook_u_(std::unique_ptr(frame)); } else { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ if (participants_[frame->ssrc]->rr_frame) { @@ -1460,7 +1460,7 @@ rtp_error_t uvgrtp::rtcp::handle_sender_report_packet(uint8_t* buffer, size_t& r sr_hook_u_(std::unique_ptr(frame)); } else { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ if (participants_[frame->ssrc]->sr_frame) { @@ -1528,7 +1528,7 @@ rtp_error_t uvgrtp::rtcp::handle_sdes_packet(uint8_t* packet, size_t& read_ptr, } else if (sdes_hook_u_) { sdes_hook_u_(std::unique_ptr(frame)); } else { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); // Deallocate previous frame from the buffer if it exists, it's going to get overwritten if (participants_[sender_ssrc]->sdes_frame) { @@ -1626,7 +1626,7 @@ rtp_error_t uvgrtp::rtcp::handle_app_packet(uint8_t* packet, size_t& read_ptr, } else if (app_hook_u_) { app_hook_u_(std::unique_ptr(frame)); } else { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if (participants_[frame->ssrc]->app_frame) { delete[] participants_[frame->ssrc]->app_frame->payload; @@ -1661,7 +1661,7 @@ rtp_error_t uvgrtp::rtcp::send_rtcp_packet_to_participants(uint8_t* frame, uint3 if (rtcp_socket_ != nullptr) { - std::lock_guard prtcp_lock(participants_mutex_); + std::lock_guard prtcp_lock(participants_mutex_); if ((ret = rtcp_socket_->sendto(socket_address_, socket_address_ipv6_, frame, frame_size, 0)) != RTP_OK) { UVG_LOG_ERROR("Sending rtcp packet with sendto() failed!"); @@ -1765,7 +1765,7 @@ rtp_error_t uvgrtp::rtcp::generate_report() bool bye_packet = !bye_ssrcs_.empty(); // Unique lock unlocks when exiting the scope - std::unique_lock prtcp_lock(participants_mutex_); + std::unique_lock prtcp_lock(participants_mutex_); uint8_t reports = 0; for (auto& p : participants_) { @@ -1777,10 +1777,11 @@ rtp_error_t uvgrtp::rtcp::generate_report() std::vector< std::shared_ptr> outgoing_apps_; if (hooked_app_) { std::lock_guard grd(send_app_mutex_); - for (auto& [name, hook] : outgoing_app_hooks_) { - uint32_t p_len = 0; - uint8_t subtype = 0; - + for (auto& p : outgoing_app_hooks_) { + uint32_t p_len = 0; + uint8_t subtype = 0; + std::string name = p.first; + auto hook = p.second; std::unique_ptr pload = hook(subtype, p_len); if (p_len > 0 && sizeof(pload.get()) != 0) { std::shared_ptr app_pkt = std::make_shared(name.data(), subtype, p_len, std::move(pload)); @@ -1872,8 +1873,13 @@ rtp_error_t uvgrtp::rtcp::generate_report() /* Calculate number of packets lost */ uint32_t lost = expected - p.second->stats.received_pkts; - lost = std::clamp(lost, uint32_t(8388607), uint32_t(8388608)); - + // clamp lost at 0x7fffff for positive loss and 0x800000 for negative loss + if (lost > 8388608) { + lost = 8388608; + } + else if (lost < 8388607) { + lost = 8388607; + } uint32_t expected_interval = expected - p.second->stats.expected_prior; p.second->stats.expected_prior = expected; uint32_t received_interval = p.second->stats.received_pkts - p.second->stats.received_prior; From bb7c0a169e42702ff554e683cdbd9d63a15171d9 Mon Sep 17 00:00:00 2001 From: Heikki Tampio Date: Fri, 28 Apr 2023 11:12:35 +0300 Subject: [PATCH 5/7] rtcp: Fix bug with send_app_packet() Payload was set incorrectly --- src/rtcp.cc | 7 ++++++- test/test_3_rtcp.cpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rtcp.cc b/src/rtcp.cc index 0b997b4..7c8bcb1 100644 --- a/src/rtcp.cc +++ b/src/rtcp.cc @@ -2025,7 +2025,12 @@ rtp_error_t uvgrtp::rtcp::send_app_packet(const char* name, uint8_t subtype, { packet_mutex_.lock(); - std::unique_ptr pl = std::make_unique(*payload); + std::unique_ptr pl = std::make_unique(payload_len); + + for (auto c = 0; c < payload_len; ++c) { + pl[c] = payload[c]; + } + if (!app_packets_[name].empty()) { UVG_LOG_DEBUG("Adding a new APP packet for sending when %llu packets are waiting to be sent", diff --git a/test/test_3_rtcp.cpp b/test/test_3_rtcp.cpp index 1cafc31..4d62e3a 100644 --- a/test/test_3_rtcp.cpp +++ b/test/test_3_rtcp.cpp @@ -271,6 +271,7 @@ void app_hook(uvgrtp::frame::rtcp_app_packet* frame) size_t payload_len = size_t(frame->header.length - 2)*4; std::string payload = std::string((char*)frame->payload, payload_len); std::string name = std::string((char*)frame->name, 4); + uint8_t subtype = uint8_t(frame->header.pkt_subtype); std::cout << std::endl << "APP frame! ----------" << std::endl; std::cout << "ssrc: " << frame->ssrc << std::endl; @@ -278,6 +279,10 @@ void app_hook(uvgrtp::frame::rtcp_app_packet* frame) std::cout << "Calculated payload length " << payload_len << std::endl; std::cout << "Payload length field " << frame->payload_len << std::endl; + EXPECT_EQ(name, "Test"); + EXPECT_EQ(payload, "ABCD"); + EXPECT_EQ(payload_len, 4); + EXPECT_EQ(subtype, 1); if (payload_len > 0) { From aba2fb8173d91036575085cdcfc89f7902882330 Mon Sep 17 00:00:00 2001 From: Heikki Tampio Date: Fri, 28 Apr 2023 11:18:22 +0300 Subject: [PATCH 6/7] rtcp: fix small bug with last commit --- src/rtcp.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtcp.cc b/src/rtcp.cc index 7c8bcb1..abbbad8 100644 --- a/src/rtcp.cc +++ b/src/rtcp.cc @@ -2027,7 +2027,7 @@ rtp_error_t uvgrtp::rtcp::send_app_packet(const char* name, uint8_t subtype, std::unique_ptr pl = std::make_unique(payload_len); - for (auto c = 0; c < payload_len; ++c) { + for (uint32_t c = 0; c < payload_len; ++c) { pl[c] = payload[c]; } From 4758d47ad575b08ba76ecbad57cc38556202eaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20R=C3=A4s=C3=A4nen?= Date: Tue, 2 May 2023 13:28:00 +0300 Subject: [PATCH 7/7] doc: Clarify the reception report field usage --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index a338393..e22e40d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -173,4 +173,4 @@ None of these parameters will however help if you are sending more data than the ## Using uvgRTP RTCP for Congestion Control -When RTCP is enabled in uvgRTP (using `RCE_RTCP`), the fields in [rtcp_report_block](../include/uvgrtp/frame.hh#L106) can be used to estimate the available network bandwidth. Report blocks are sent by all media_stream entities receiving data and can be included in both Sender Reports (when sending and receiving) and Receiver Reports (when only receiving). There exists several algorithms for congestion control, but they are outside the scope of uvgRTP. +When RTCP is enabled in uvgRTP (using `RCE_RTCP`); fraction, lost and jitter fields in [rtcp_report_block](../include/uvgrtp/frame.hh#L106) can be used to detect network congestion. Report blocks are sent by all media_stream entities receiving data and can be included in both Sender Reports (when sending and receiving) and Receiver Reports (when only receiving). There exists several algorithms for congestion control, but they are outside the scope of uvgRTP.