rtcp: Calculate compound packet size correctly for app and bye

This commit is contained in:
Joni Räsänen 2022-07-15 10:56:31 +03:00
parent 202209fe8e
commit 177a0b0d83
5 changed files with 151 additions and 51 deletions

View File

@ -298,10 +298,18 @@ namespace uvgrtp {
// the length field is the rtcp packet size measured in 32-bit words - 1
size_t rtcp_length_in_bytes(uint16_t length);
/// \cond DO_NOT_DOCUMENT
void set_mtu_size(size_t mtu_size);
private:
rtp_error_t set_sdes_items(const std::vector<uvgrtp::frame::rtcp_sdes_item>& items);
size_t size_of_ready_app_packets() const;
size_t size_of_compound_packet(uint16_t reports,
bool sr_packet, bool rr_packet, bool sdes_packet, size_t app_size, bool bye_packet) const;
/* read the header values from rtcp packet */
void read_rtcp_header(const uint8_t* buffer, size_t& read_ptr,
uvgrtp::frame::rtcp_header& header);
@ -491,6 +499,8 @@ namespace uvgrtp {
uvgrtp::frame::rtcp_sdes_item cnameItem_;
char cname_[255];
size_t mtu_size_;
};
}

View File

@ -566,6 +566,7 @@ rtp_error_t uvgrtp::media_stream::configure_ctx(int flag, ssize_t value)
}
rtp_->set_payload_size(value - hdr);
rtcp_->set_mtu_size(value - (ETH_HDR_SIZE + IPV4_HDR_SIZE + UDP_HDR_SIZE));
}
break;

View File

@ -54,7 +54,8 @@ uvgrtp::rtcp::rtcp(std::shared_ptr<uvgrtp::rtp> rtp, std::string cname, int flag
active_(false),
interval_ms_(DEFAULT_RTCP_INTERVAL_MS),
ourItems_(),
bye_ssrcs_(false)
bye_ssrcs_(false),
mtu_size_(MAX_PAYLOAD)
{
clock_rate_ = rtp->get_clock_rate();
@ -1471,8 +1472,61 @@ rtp_error_t uvgrtp::rtcp::send_rtcp_packet_to_participants(uint8_t* frame, size_
return ret;
}
size_t uvgrtp::rtcp::size_of_ready_app_packets() const
{
int ready_packets = 0;
for (auto& app_name : app_packets_)
{
// TODO: Should we also send one per subtype?
if (!app_name.second.empty())
{
++ready_packets;
}
}
return ready_packets;
}
size_t uvgrtp::rtcp::size_of_compound_packet(uint16_t reports,
bool sr_packet, bool rr_packet, bool sdes_packet, size_t app_size, bool bye_packet) const
{
size_t compound_packet_size = 0;
if (sr_packet)
{
compound_packet_size = get_sr_packet_size(flags_, reports);
}
else if (rr_packet)
{
compound_packet_size = get_rr_packet_size(flags_, reports);
}
else
{
LOG_ERROR("RTCP compound packet must start with either SR or RR");
return 0;
}
if (sdes_packet)
{
compound_packet_size += get_sdes_packet_size(ourItems_);
}
if (app_size != 0)
{
compound_packet_size += app_size;
}
if (bye_packet)
{
compound_packet_size += get_bye_packet_size(bye_ssrcs_);
}
return compound_packet_size;
}
rtp_error_t uvgrtp::rtcp::generate_report()
{
packet_mutex_.lock();
rtcp_pkt_sent_count_++;
uint16_t reports = 0;
@ -1484,14 +1538,24 @@ rtp_error_t uvgrtp::rtcp::generate_report()
}
}
size_t compound_packet_size = get_rr_packet_size(flags_, reports);
bool sr_packet = our_role_ == SENDER && our_stats.sent_rtp_packet;
bool rr_packet = our_role_ == RECEIVER || our_stats.sent_rtp_packet == 0;
bool sdes_packet = true;
size_t app_packets_size = size_of_ready_app_packets();
bool bye_packet = !bye_ssrcs_.empty();
if (our_role_ == SENDER && our_stats.sent_rtp_packet)
size_t compound_packet_size = size_of_compound_packet(reports, sr_packet, rr_packet, sdes_packet, app_packets_size, bye_packet);
if (compound_packet_size == 0)
{
compound_packet_size = get_sr_packet_size(flags_, reports);
LOG_WARN("Failed to get compound packet size");
return RTP_GENERIC_ERROR;
}
if (compound_packet_size > mtu_size_)
{
LOG_WARN("Generate RTCP packet is too large %lli/%lli, reports should be circled, but not implemented!",
compound_packet_size, mtu_size_);
}
compound_packet_size += get_sdes_packet_size(ourItems_);
uint8_t* frame = new uint8_t[compound_packet_size];
memset(frame, 0, compound_packet_size);
@ -1499,7 +1563,7 @@ rtp_error_t uvgrtp::rtcp::generate_report()
// see https://datatracker.ietf.org/doc/html/rfc3550#section-6.4.1
int write_ptr = 0;
if (our_role_ == SENDER && our_stats.sent_rtp_packet)
if (sr_packet)
{
// sender reports have sender information in addition compared to receiver reports
size_t sender_report_size = get_sr_packet_size(flags_, reports);
@ -1519,21 +1583,31 @@ rtp_error_t uvgrtp::rtcp::generate_report()
uint64_t rtp_ts = rtp_ts_start_ + (uvgrtp::clock::ntp::diff(clock_start_, ntp_ts))
* float(clock_rate_ / 1000);
// TODO: Return error if these fail
construct_rtcp_header(frame, write_ptr, sender_report_size, reports, uvgrtp::frame::RTCP_FT_SR);
construct_ssrc(frame, write_ptr, ssrc_);
construct_sender_info(frame, write_ptr, ntp_ts, rtp_ts, our_stats.sent_pkts, our_stats.sent_bytes);
if (!construct_rtcp_header(frame, write_ptr, sender_report_size, reports, uvgrtp::frame::RTCP_FT_SR) ||
!construct_ssrc(frame, write_ptr, ssrc_) ||
!construct_sender_info(frame, write_ptr, ntp_ts, rtp_ts, our_stats.sent_pkts, our_stats.sent_bytes))
{
LOG_ERROR("Failed to construct SR");
return RTP_GENERIC_ERROR;
}
our_stats.sent_rtp_packet = false;
} else { // RECEIVER
} else if (rr_packet) { // RECEIVER
size_t receiver_report_size = get_rr_packet_size(flags_, reports);
LOG_DEBUG("Generating RTCP Receiver report size: %li", receiver_report_size);
// TODO: Return error if these fail
construct_rtcp_header(frame, write_ptr, receiver_report_size, reports, uvgrtp::frame::RTCP_FT_RR);
construct_ssrc(frame, write_ptr, ssrc_);
if (!construct_rtcp_header(frame, write_ptr, receiver_report_size, reports, uvgrtp::frame::RTCP_FT_RR) ||
!construct_ssrc(frame, write_ptr, ssrc_))
{
LOG_ERROR("Failed to construct RR");
return RTP_GENERIC_ERROR;
}
}
else
{
// SR or RR is mandatory at the beginning
return RTP_GENERIC_ERROR;
}
// the report blocks for sender or receiver report. Both have same reports.
@ -1565,48 +1639,49 @@ rtp_error_t uvgrtp::rtcp::generate_report()
}
}
packet_mutex_.lock();
// add the SDES packet after the SR/RR, mandatory, must contain CNAME
if (!construct_rtcp_header(frame, write_ptr, get_sdes_packet_size(ourItems_), num_receivers_,
uvgrtp::frame::RTCP_FT_SDES) ||
!construct_sdes_chunk(frame, write_ptr, { ssrc_, ourItems_ }))
if (sdes_packet)
{
LOG_ERROR("Failed to add SDES packet");
delete[] frame;
return RTP_GENERIC_ERROR;
// add the SDES packet after the SR/RR, mandatory, must contain CNAME
if (!construct_rtcp_header(frame, write_ptr, get_sdes_packet_size(ourItems_), num_receivers_,
uvgrtp::frame::RTCP_FT_SDES) ||
!construct_sdes_chunk(frame, write_ptr, { ssrc_, ourItems_ }))
{
LOG_ERROR("Failed to add SDES packet");
delete[] frame;
return RTP_GENERIC_ERROR;
}
}
LOG_DEBUG("Sending RTCP report compound packet, Total size: %lli, SDES packet size: %lli",
compound_packet_size, get_sdes_packet_size(ourItems_));
for (auto& app_name : app_packets_)
if (app_packets_size != 0)
{
// we send one packet per APP name
// TODO: Should we also send one per subtype?
if (!app_name.second.empty())
for (auto& app_name : app_packets_)
{
// take the oldest APP packet and send it
rtcp_app_packet next_packet = app_name.second.front();
app_name.second.pop_front();
// we send one packet per APP name
uint16_t secondField = (next_packet.subtype & 0x1f);
if (!construct_rtcp_header(frame, write_ptr, get_app_packet_size(next_packet.payload_len), secondField,
uvgrtp::frame::RTCP_FT_APP) ||
!construct_ssrc(frame, write_ptr, ssrc_) ||
!construct_app_packet(frame, write_ptr, next_packet.name, next_packet.payload, next_packet.payload_len))
// TODO: Should we also send one per subtype?
if (!app_name.second.empty())
{
delete[] frame;
return RTP_GENERIC_ERROR;
// take the oldest APP packet and send it
rtcp_app_packet next_packet = app_name.second.front();
app_name.second.pop_front();
uint16_t secondField = (next_packet.subtype & 0x1f);
if (!construct_rtcp_header(frame, write_ptr, get_app_packet_size(next_packet.payload_len), secondField,
uvgrtp::frame::RTCP_FT_APP) ||
!construct_ssrc(frame, write_ptr, ssrc_) ||
!construct_app_packet(frame, write_ptr, next_packet.name, next_packet.payload, next_packet.payload_len))
{
LOG_ERROR("Failed to construct APP packet");
delete[] frame;
return RTP_GENERIC_ERROR;
}
}
}
}
// BYE is always last
if (!bye_ssrcs_.empty())
// BYE is last if it is sent
if (bye_packet)
{
// header construction does not add our ssrc for BYE
uint16_t secondField = (bye_ssrcs_.size() & 0x1f);
@ -1615,6 +1690,7 @@ rtp_error_t uvgrtp::rtcp::generate_report()
!construct_bye_packet(frame, write_ptr, bye_ssrcs_))
{
bye_ssrcs_.clear();
LOG_ERROR("Failed to construct BYE");
delete[] frame;
return RTP_GENERIC_ERROR;
}
@ -1623,6 +1699,9 @@ rtp_error_t uvgrtp::rtcp::generate_report()
}
packet_mutex_.unlock();
LOG_DEBUG("Sending RTCP report compound packet, Total size: %lli",
compound_packet_size);
return send_rtcp_packet_to_participants(frame, compound_packet_size, true);
}
@ -1681,4 +1760,10 @@ void uvgrtp::rtcp::set_session_bandwidth(int kbps)
interval_ms_ = DEFAULT_RTCP_INTERVAL_MS;
}
// TODO: This should follow the algorithm specified in RFC 3550 appendix-A.7
}
void uvgrtp::rtcp::set_mtu_size(size_t mtu_size)
{
mtu_size_ = mtu_size;
}

View File

@ -92,7 +92,7 @@ bool uvgrtp::construct_ssrc(uint8_t* frame, int& ptr, uint32_t ssrc)
return true;
}
void uvgrtp::construct_sender_info(uint8_t* frame, int& ptr, uint64_t ntp_ts, uint64_t rtp_ts,
bool uvgrtp::construct_sender_info(uint8_t* frame, int& ptr, uint64_t ntp_ts, uint64_t rtp_ts,
uint32_t sent_packets, uint32_t sent_bytes)
{
SET_NEXT_FIELD_32(frame, ptr, htonl(ntp_ts >> 32)); // NTP ts msw
@ -100,9 +100,11 @@ void uvgrtp::construct_sender_info(uint8_t* frame, int& ptr, uint64_t ntp_ts, ui
SET_NEXT_FIELD_32(frame, ptr, htonl((uint32_t)rtp_ts)); // RTP ts
SET_NEXT_FIELD_32(frame, ptr, htonl(sent_packets)); // sender's packet count
SET_NEXT_FIELD_32(frame, ptr, htonl(sent_bytes)); // sender's octet count (not bytes)
return true;
}
void uvgrtp::construct_report_block(uint8_t* frame, int& ptr, uint32_t ssrc, uint8_t fraction,
bool uvgrtp::construct_report_block(uint8_t* frame, int& ptr, uint32_t ssrc, uint8_t fraction,
uint32_t dropped_packets, uint16_t seq_cycles, uint16_t max_seq, uint32_t jitter,
uint32_t lsr, uint32_t dlsr)
{
@ -112,6 +114,8 @@ void uvgrtp::construct_report_block(uint8_t* frame, int& ptr, uint32_t ssrc, uin
SET_NEXT_FIELD_32(frame, ptr, htonl(jitter));
SET_NEXT_FIELD_32(frame, ptr, htonl(lsr));
SET_NEXT_FIELD_32(frame, ptr, htonl(dlsr));
return true;
}
bool uvgrtp::construct_app_packet(uint8_t* frame, int& ptr,

View File

@ -27,11 +27,11 @@ namespace uvgrtp
bool construct_ssrc(uint8_t* frame, int& ptr, uint32_t ssrc);
// Add sender info for sender report
void construct_sender_info(uint8_t* frame, int& ptr,
bool construct_sender_info(uint8_t* frame, int& ptr,
uint64_t ntp_ts, uint64_t rtp_ts, uint32_t sent_packets, uint32_t sent_bytes);
// Add one report block for sender or receiver report
void construct_report_block(uint8_t* frame, int& ptr, uint32_t ssrc, uint8_t fraction,
bool construct_report_block(uint8_t* frame, int& ptr, uint32_t ssrc, uint8_t fraction,
uint32_t dropped_packets, uint16_t seq_cycles, uint16_t max_seq, uint32_t jitter,
uint32_t lsr, uint32_t dlsr);