Merge branch 'master' of https://gitlab.tuni.fi/cs/ultravideo/uvgrtp into multiplex

This commit is contained in:
Heikki Tampio 2023-05-03 15:09:43 +03:00
commit d7058235dd
8 changed files with 321 additions and 38 deletions

View File

@ -148,7 +148,7 @@ target_link_libraries(${PROJECT_NAME}
target_include_directories(${PROJECT_NAME}
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
PUBLIC $<INSTALL_INTERFACE:include>
)

View File

@ -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.

View File

@ -736,7 +736,7 @@ rtp_error_t uvgrtp::rtcp::install_app_hook(std::function<void(std::unique_ptr<uv
uvgrtp::frame::rtcp_sender_report* uvgrtp::rtcp::get_sender_packet(uint32_t ssrc)
{
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
return nullptr;
@ -752,7 +752,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
return nullptr;
@ -768,7 +768,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
return nullptr;
@ -784,7 +784,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
return nullptr;
@ -805,7 +805,7 @@ std::vector<uint32_t> uvgrtp::rtcp::get_participants() const
for (auto& i : participants_)
{
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> prtcp_lock(participants_mutex_);
ssrcs.push_back(i.first);
}
@ -862,7 +862,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<std::mutex> prtcp_lock(participants_mutex_);
return participants_.find(ssrc) != participants_.end();
}
@ -956,7 +956,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
return RTP_NOT_FOUND;
@ -971,7 +971,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) == participants_.end())
{
UVG_LOG_ERROR("Did not find participant SSRC when updating seq");
@ -1033,7 +1033,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<std::mutex> prtcp_lock(participants_mutex_);
if (participants_.find(ssrc) != participants_.end())
{
return RTP_SSRC_COLLISION;
@ -1046,13 +1046,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<std::mutex> 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<std::mutex> prtcp_lock(participants_mutex_);
participants_[frame->header.ssrc]->stats.received_rtp_packet = true;
participants_[frame->header.ssrc]->stats.received_pkts += 1;
@ -1385,7 +1385,7 @@ rtp_error_t uvgrtp::rtcp::handle_receiver_report_packet(uint8_t* buffer, size_t&
rr_hook_u_(std::unique_ptr<uvgrtp::frame::rtcp_receiver_report>(frame));
}
else {
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> 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)
{
@ -1439,7 +1439,7 @@ rtp_error_t uvgrtp::rtcp::handle_sender_report_packet(uint8_t* buffer, size_t& r
sr_hook_u_(std::unique_ptr<uvgrtp::frame::rtcp_sender_report>(frame));
}
else {
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> 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)
{
@ -1507,7 +1507,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<uvgrtp::frame::rtcp_sdes_packet>(frame));
} else {
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> 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)
{
@ -1605,7 +1605,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<uvgrtp::frame::rtcp_app_packet>(frame));
} else {
std::lock_guard prtcp_lock(participants_mutex_);
std::lock_guard<std::mutex> prtcp_lock(participants_mutex_);
if (participants_[frame->ssrc]->app_frame)
{
delete[] participants_[frame->ssrc]->app_frame->payload;
@ -1640,7 +1640,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<std::mutex> 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!");
@ -1744,7 +1744,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<std::mutex> prtcp_lock(participants_mutex_);
uint8_t reports = 0;
for (auto& p : participants_)
{
@ -1756,10 +1756,11 @@ rtp_error_t uvgrtp::rtcp::generate_report()
std::vector< std::shared_ptr<rtcp_app_packet>> outgoing_apps_;
if (hooked_app_) {
std::lock_guard<std::mutex> 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<uint8_t[]> pload = hook(subtype, p_len);
if (p_len > 0 && sizeof(pload.get()) != 0) {
std::shared_ptr<rtcp_app_packet> app_pkt = std::make_shared<rtcp_app_packet>(name.data(), subtype, p_len, std::move(pload));
@ -1851,8 +1852,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;
@ -1998,7 +2004,12 @@ rtp_error_t uvgrtp::rtcp::send_app_packet(const char* name, uint8_t subtype,
{
packet_mutex_.lock();
std::unique_ptr<uint8_t[]> pl = std::make_unique<uint8_t[]>(*payload);
std::unique_ptr<uint8_t[]> pl = std::make_unique<uint8_t[]>(payload_len);
for (uint32_t 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",

View File

@ -110,38 +110,140 @@ 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
// 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);
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
// 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();
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("Reuse address failed!");
}
// 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();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
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;

View File

@ -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,65 @@ TEST(RTPTests, send_large_amounts)
cleanup_ms(sess, sender);
cleanup_ms(sess, receiver);
cleanup_sess(ctx, sess);
}
}
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<size_t> sizes = { 1000, 2000 };
for (size_t& size : sizes)
{
std::unique_ptr<uint8_t[]> 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);
}
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<uvgrtp::media_stream*> 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<size_t> sizes = { 1000, 2000 };
for (size_t& size : sizes)
{
std::unique_ptr<uint8_t[]> 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);
}

View File

@ -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)
{

View File

@ -9,6 +9,8 @@ constexpr uint16_t SEND_PORT = 9300;
constexpr char REMOTE_ADDRESS[] = "::1";
constexpr uint16_t RECEIVE_PORT = 9400;
constexpr char MULTICAST_ADDRESS[] = "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,65 @@ void zrtp_receive_func6(uvgrtp::session* receiver_session, int sender_port, int
}
cleanup_ms(receiver_session, recv);
}
}
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_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<size_t> sizes = { 1000, 2000 };
for (size_t& size : sizes)
{
std::unique_ptr<uint8_t[]> 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);
}
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<uvgrtp::media_stream*> 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<size_t> sizes = { 1000, 2000 };
for (size_t& size : sizes)
{
std::unique_ptr<uint8_t[]> 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);
}

View File

@ -14,6 +14,11 @@ inline std::unique_ptr<uint8_t[]> create_test_packet(rtp_format_t format, uint8_
inline void test_packet_size(std::unique_ptr<uint8_t[]> 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<uint8_t[]> test_packet, int packets, size_t size,
uvgrtp::session* sess,
uvgrtp::media_stream* sender, std::vector<uvgrtp::media_stream*> const& receiver,
int rtp_flags, int framerate = 25);
inline void send_packets(std::unique_ptr<uint8_t[]> 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<uint8_t[]> test_packet, int packets
}
}
inline void test_packet_size(std::unique_ptr<uint8_t[]> test_packet, int packets, size_t size,
uvgrtp::session* sess, uvgrtp::media_stream* sender,
std::vector<uvgrtp::media_stream*> const& receivers,
int rtp_flags, int framerate)
{
EXPECT_NE(nullptr, sess);
EXPECT_NE(nullptr, sender);
if (sess && sender)
{
std::vector<Test_receiver> 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*))
{