Merge branch 'master' of https://gitlab.tuni.fi/cs/ultravideo/uvgrtp into multiplex
This commit is contained in:
commit
d7058235dd
|
@ -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>
|
||||
)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
59
src/rtcp.cc
59
src/rtcp.cc
|
@ -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",
|
||||
|
|
122
src/socket.cc
122
src/socket.cc
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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*))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue