From 97baf19437b7b487dcb958d8d6cbdba46ff8156d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20R=C3=A4s=C3=A4nen?= Date: Sat, 9 Jul 2022 17:41:54 +0300 Subject: [PATCH] rtcp: Refactor rtcp so compound packets are read correctly --- include/uvgrtp/frame.hh | 12 +- include/uvgrtp/rtcp.hh | 28 +- src/rtcp.cc | 572 +++++++++++++++++++++------------------- src/rtcp_packets.cc | 12 +- src/rtcp_packets.hh | 3 +- 5 files changed, 331 insertions(+), 296 deletions(-) diff --git a/include/uvgrtp/frame.hh b/include/uvgrtp/frame.hh index 424059c..d97c2a2 100644 --- a/include/uvgrtp/frame.hh +++ b/include/uvgrtp/frame.hh @@ -13,10 +13,6 @@ #include #include - -#define RTP_HEADER_LENGTH 12 -#define RTCP_HEADER_LENGTH 12 - namespace uvgrtp { namespace frame { enum HEADER_SIZES { @@ -144,12 +140,16 @@ namespace uvgrtp { void *data = nullptr; }; - struct rtcp_sdes_packet { - struct rtcp_header header; + struct rtcp_sdes_chunk { uint32_t ssrc = 0; std::vector items; }; + struct rtcp_sdes_packet { + struct rtcp_header header; + std::vector chunks; + }; + struct rtcp_app_packet { struct rtcp_header header; uint32_t ssrc = 0; diff --git a/include/uvgrtp/rtcp.hh b/include/uvgrtp/rtcp.hh index 5904ed3..15559b9 100644 --- a/include/uvgrtp/rtcp.hh +++ b/include/uvgrtp/rtcp.hh @@ -288,19 +288,31 @@ namespace uvgrtp { static rtp_error_t send_packet_handler_vec(void *arg, uvgrtp::buf_vec& buffers); /// \endcond + // the length field is the rtcp packet size measured in 32-bit words - 1 + size_t rtcp_length_in_bytes(uint16_t length); + private: + /* read the header values from rtcp packet */ + void read_rtcp_header(const uint8_t* buffer, size_t& read_ptr, + uvgrtp::frame::rtcp_header& header); + void read_reports(const uint8_t* buffer, size_t& read_ptr, size_t packet_end, uint8_t count, + std::vector& reports); + + void read_ssrc(const uint8_t* buffer, size_t& read_ptr, uint32_t& out_ssrc); + /* Handle different kinds of incoming rtcp packets. The read header is passed to functions which read rest of the frame type specific data. * Return RTP_OK on success and RTP_ERROR on error */ - rtp_error_t handle_sender_report_packet(uint8_t* frame, size_t size, + rtp_error_t handle_sender_report_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, uvgrtp::frame::rtcp_header& header); - rtp_error_t handle_receiver_report_packet(uint8_t* frame, size_t size, + rtp_error_t handle_receiver_report_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, uvgrtp::frame::rtcp_header& header); - rtp_error_t handle_sdes_packet(uint8_t* frame, size_t size, + rtp_error_t handle_sdes_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, + uvgrtp::frame::rtcp_header& header, uint32_t sender_ssrc); + rtp_error_t handle_bye_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, uvgrtp::frame::rtcp_header& header); - rtp_error_t handle_bye_packet(uint8_t* frame, size_t size); - rtp_error_t handle_app_packet(uint8_t* frame, size_t size, + rtp_error_t handle_app_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, uvgrtp::frame::rtcp_header& header); static void rtcp_runner(rtcp *rtcp, int interval); @@ -349,15 +361,9 @@ namespace uvgrtp { void zero_stats(uvgrtp::receiver_statistics *stats); - /* read the header values from rtcp packet */ - void read_rtcp_header(const uint8_t* packet, uvgrtp::frame::rtcp_header& header); - void read_reports(const uint8_t* packet, size_t size, uint8_t count, bool has_sender_block, - std::vector& reports); - /* Takes ownership of the frame */ rtp_error_t send_rtcp_packet_to_participants(uint8_t* frame, size_t frame_size, bool encrypt); - void free_participant(rtcp_participant* participant); /* Secure RTCP context */ diff --git a/src/rtcp.cc b/src/rtcp.cc index aafeaa2..09226e4 100644 --- a/src/rtcp.cc +++ b/src/rtcp.cc @@ -137,9 +137,11 @@ rtp_error_t uvgrtp::rtcp::start() rtp_error_t uvgrtp::rtcp::stop() { + LOG_DEBUG("Stopping RTCP"); // TODO: Make thread safe. I think this kind of works, but not in a flexible way if (!active_) { + LOG_DEBUG("Removing all participants"); /* free all receiver statistic structs */ for (auto& participant : participants_) { @@ -199,13 +201,13 @@ void uvgrtp::rtcp::rtcp_runner(rtcp* rtcp, int interval) long int run_time = uvgrtp::clock::hrc::diff_now(start); long int diff_ms = next_sendslot - run_time; + rtp_error_t ret = RTP_OK; if (diff_ms <= 0) { ++i; LOG_DEBUG("Sending RTCP report number %i at time slot %i ms", i, next_sendslot); - rtp_error_t ret = RTP_OK; if ((ret = rtcp->generate_report()) != RTP_OK && ret != RTP_NOT_READY) { LOG_ERROR("Failed to send RTCP status report!"); @@ -223,8 +225,7 @@ void uvgrtp::rtcp::rtcp_runner(rtcp* rtcp, int interval) poll_timout = max_poll_timeout_ms; } - rtp_error_t ret = uvgrtp::poll::poll(rtcp->get_sockets(), buffer, MAX_PACKET, - poll_timout, &nread); + ret = uvgrtp::poll::poll(rtcp->get_sockets(), buffer, MAX_PACKET, poll_timout, &nread); if (ret == RTP_OK && nread > 0) { @@ -232,7 +233,7 @@ void uvgrtp::rtcp::rtcp_runner(rtcp* rtcp, int interval) } else if (ret == RTP_INTERRUPTED) { /* do nothing */ } else { - LOG_ERROR("recvfrom failed, %d", ret); + LOG_ERROR("poll failed, %d", ret); } } else { // sleep until it is time to send the report std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms)); @@ -977,30 +978,60 @@ rtp_error_t uvgrtp::rtcp::send_packet_handler_vec(void *arg, uvgrtp::buf_vec& bu return ((uvgrtp::rtcp *)arg)->update_sender_stats(pkt_size); } +size_t uvgrtp::rtcp::rtcp_length_in_bytes(uint16_t length) +{ + // the length field is the rtcp packet size measured in 32-bit words - 1 + return uint32_t(length + 1) * sizeof(uint32_t); +} + rtp_error_t uvgrtp::rtcp::handle_incoming_packet(uint8_t *buffer, size_t size) { - size_t ptr = 0; + if (!buffer || !size) + { + return RTP_INVALID_VALUE; + } + + size_t read_ptr = 0; size_t remaining_size = size; int packets = 0; + update_rtcp_bandwidth(size); + + rtp_error_t ret = RTP_OK; + + uint32_t sender_ssrc = 0; + + // decrypt the whole compound packet + if (size > RTCP_HEADER_SIZE + SSRC_CSRC_SIZE) + { + sender_ssrc = ntohl(*(uint32_t*)& buffer[read_ptr]); + if (srtcp_ && (ret = srtcp_->handle_rtcp_decryption(flags_, sender_ssrc, buffer + read_ptr, size)) != RTP_OK) + { + LOG_ERROR("Failed at decryption"); + return ret; + } + } + // this handles each separate rtcp packet in a compound packet while (remaining_size > 0) { ++packets; - if (remaining_size < RTCP_HEADER_LENGTH) + if (remaining_size < RTCP_HEADER_SIZE) { - LOG_ERROR("Didn't get enough data for an rtcp header. Packet # %i Got data: %lli", packets, remaining_size); + LOG_ERROR("Didn't get enough data for an rtcp header. Packet # %i Got data: %lli", + packets, remaining_size); return RTP_INVALID_VALUE; } - uint8_t* packet_location = buffer + ptr; - uvgrtp::frame::rtcp_header header; - read_rtcp_header(packet_location, header); + read_rtcp_header(buffer, read_ptr, header); - // the length field is the rtcp packet size measured in 32-bit words - 1 - uint32_t size_of_rtcp_packet = (header.length + 1) * sizeof(uint32_t); + uint32_t size_of_rtcp_packet = rtcp_length_in_bytes(header.length); + + /* Possible packet padding means header.length is only reliable way to find next packet. + * We have to substract the size of header, since it was added when reading the header. */ + size_t packet_end = read_ptr - RTCP_HEADER_SIZE + size_of_rtcp_packet; if (remaining_size < size_of_rtcp_packet) { @@ -1027,30 +1058,28 @@ rtp_error_t uvgrtp::rtcp::handle_incoming_packet(uint8_t *buffer, size_t size) return RTP_INVALID_VALUE; } - update_rtcp_bandwidth(size_of_rtcp_packet); - rtp_error_t ret = RTP_INVALID_VALUE; switch (header.pkt_type) { case uvgrtp::frame::RTCP_FT_SR: - ret = handle_sender_report_packet(packet_location, size_of_rtcp_packet, header); + ret = handle_sender_report_packet(buffer, read_ptr, packet_end, header); break; case uvgrtp::frame::RTCP_FT_RR: - ret = handle_receiver_report_packet(packet_location, size_of_rtcp_packet, header); + ret = handle_receiver_report_packet(buffer, read_ptr, packet_end, header); break; case uvgrtp::frame::RTCP_FT_SDES: - ret = handle_sdes_packet(packet_location, size_of_rtcp_packet, header); + ret = handle_sdes_packet(buffer, read_ptr, packet_end, header, sender_ssrc); break; case uvgrtp::frame::RTCP_FT_BYE: - ret = handle_bye_packet(packet_location, size_of_rtcp_packet); + ret = handle_bye_packet(buffer, read_ptr, packet_end, header); break; case uvgrtp::frame::RTCP_FT_APP: - ret = handle_app_packet(packet_location, size_of_rtcp_packet, header); + ret = handle_app_packet(buffer, read_ptr, packet_end, header); break; default: @@ -1063,7 +1092,7 @@ rtp_error_t uvgrtp::rtcp::handle_incoming_packet(uint8_t *buffer, size_t size) return ret; } - ptr += size_of_rtcp_packet; + read_ptr = packet_end; remaining_size -= size_of_rtcp_packet; } @@ -1074,37 +1103,191 @@ rtp_error_t uvgrtp::rtcp::handle_incoming_packet(uint8_t *buffer, size_t size) return RTP_OK; } -rtp_error_t uvgrtp::rtcp::handle_sdes_packet(uint8_t* packet, size_t size, +void uvgrtp::rtcp::read_rtcp_header(const uint8_t* buffer, size_t& read_ptr, uvgrtp::frame::rtcp_header& header) +{ + header.version = (buffer[read_ptr] >> 6) & 0x3; + header.padding = (buffer[read_ptr] >> 5) & 0x1; + + header.pkt_type = buffer[read_ptr + 1] & 0xff; + + if (header.pkt_type == uvgrtp::frame::RTCP_FT_APP) + { + header.pkt_subtype = buffer[read_ptr] & 0x1f; + } + else { + header.count = buffer[read_ptr] & 0x1f; + } + + header.length = ntohs(*(uint16_t*)& buffer[read_ptr + 2]); + + read_ptr += RTCP_HEADER_SIZE; +} + +void uvgrtp::rtcp::read_reports(const uint8_t* buffer, size_t& read_ptr, size_t packet_end, uint8_t count, + std::vector& reports) +{ + for (int i = 0; i < count; ++i) + { + if (packet_end >= read_ptr + REPORT_BLOCK_SIZE) + { + uvgrtp::frame::rtcp_report_block report; + report.ssrc = ntohl(*(uint32_t*)& buffer[read_ptr + 0]); + report.fraction = (ntohl(*(uint32_t*)& buffer[read_ptr + 4])) >> 24; + report.lost = (ntohl(*(int32_t*)& buffer[read_ptr + 4])) & 0xfffffd; + report.last_seq = ntohl(*(uint32_t*)& buffer[read_ptr + 8]); + report.jitter = ntohl(*(uint32_t*)& buffer[read_ptr + 12]); + report.lsr = ntohl(*(uint32_t*)& buffer[read_ptr + 16]); + report.dlsr = ntohl(*(uint32_t*)& buffer[read_ptr + 20]); + + reports.push_back(report); + read_ptr += REPORT_BLOCK_SIZE; + } + else { + LOG_WARN("Received rtcp packet is smaller than the indicated number of reports!" + "Read: %i/%i, Read ptr: %i, Packet End:", i, count, read_ptr, packet_end); + } + } +} + +void uvgrtp::rtcp::read_ssrc(const uint8_t* buffer, size_t& read_ptr, uint32_t& out_ssrc) +{ + out_ssrc = ntohl(*(uint32_t*)& buffer[read_ptr]); + read_ptr += SSRC_CSRC_SIZE; +} + +rtp_error_t uvgrtp::rtcp::handle_receiver_report_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, uvgrtp::frame::rtcp_header& header) { - if (!packet || !size) + auto frame = new uvgrtp::frame::rtcp_receiver_report; + frame->header = header; + read_ssrc(buffer, read_ptr, frame->ssrc); + + /* Receiver Reports are sent from participant that don't send RTP packets + * This means that the sender of this report is not in the participants_ map + * but rather in the initial_participants_ vector + * + * Check if that's the case and if so, move the entry from initial_participants_ to participants_ */ + if (!is_participant(frame->ssrc)) { + LOG_WARN("Got a Receiver Report from an unknown participant"); + add_participant(frame->ssrc); + } + + if (!frame->header.count) + { + LOG_ERROR("Receiver Report cannot have 0 report blocks!"); return RTP_INVALID_VALUE; } - auto frame = new uvgrtp::frame::rtcp_sdes_packet; - frame->header = header; - frame->ssrc = ntohl(*(uint32_t*)&packet[4]); + read_reports(buffer, read_ptr, packet_end, frame->header.count, frame->report_blocks); - auto ret = RTP_OK; - if (srtcp_ && (ret = srtcp_->handle_rtcp_decryption(flags_, frame->ssrc, packet, size)) != RTP_OK) + rr_mutex_.lock(); + if (receiver_hook_) { + receiver_hook_(frame); + } + else if (rr_hook_f_) { + rr_hook_f_(std::shared_ptr(frame)); + } + else if (rr_hook_u_) { + rr_hook_u_(std::unique_ptr(frame)); + } + else { + /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ + if (participants_[frame->ssrc]->rr_frame) + { + delete participants_[frame->ssrc]->rr_frame; + } + + participants_[frame->ssrc]->rr_frame = frame; + } + rr_mutex_.unlock(); + + return RTP_OK; +} + +rtp_error_t uvgrtp::rtcp::handle_sender_report_packet(uint8_t* buffer, size_t& read_ptr, size_t packet_end, + uvgrtp::frame::rtcp_header& header) +{ + auto frame = new uvgrtp::frame::rtcp_sender_report; + frame->header = header; + read_ssrc(buffer, read_ptr, frame->ssrc); + if (!is_participant(frame->ssrc)) { - delete frame; - return ret; + LOG_INFO("Sender Report received from a new participant"); + add_participant(frame->ssrc); } - uint32_t rtcp_packet_size = (frame->header.length + 1) * sizeof(uint32_t); + participants_[frame->ssrc]->stats.sr_ts = uvgrtp::clock::hrc::now(); - for (int ptr = 8; ptr < rtcp_packet_size; ) + frame->sender_info.ntp_msw = ntohl(*(uint32_t*)& buffer[read_ptr]); + frame->sender_info.ntp_lsw = ntohl(*(uint32_t*)& buffer[read_ptr + 4]); + frame->sender_info.rtp_ts = ntohl(*(uint32_t*)& buffer[read_ptr + 8]); + frame->sender_info.pkt_cnt = ntohl(*(uint32_t*)& buffer[read_ptr + 12]); + frame->sender_info.byte_cnt = ntohl(*(uint32_t*)& buffer[read_ptr + 16]); + read_ptr += SENDER_INFO_SIZE; + + participants_[frame->ssrc]->stats.lsr = + ((frame->sender_info.ntp_msw & 0xffff) << 16) | + (frame->sender_info.ntp_lsw >> 16); + + read_reports(buffer, read_ptr, packet_end, frame->header.count, frame->report_blocks); + + sr_mutex_.lock(); + if (sender_hook_) { + sender_hook_(frame); + } + else if (sr_hook_f_) { + sr_hook_f_(std::shared_ptr(frame)); + } + else if (sr_hook_u_) { + sr_hook_u_(std::unique_ptr(frame)); + } + else { + /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ + if (participants_[frame->ssrc]->sr_frame) + { + delete participants_[frame->ssrc]->sr_frame; + } + + participants_[frame->ssrc]->sr_frame = frame; + } + sr_mutex_.unlock(); + + return RTP_OK; +} + + +rtp_error_t uvgrtp::rtcp::handle_sdes_packet(uint8_t* packet, size_t& read_ptr, size_t packet_end, + uvgrtp::frame::rtcp_header& header, uint32_t sender_ssrc) +{ + auto frame = new uvgrtp::frame::rtcp_sdes_packet; + frame->header = header; + + // Read SDES chunks + while (read_ptr + SSRC_CSRC_SIZE <= packet_end) { - uvgrtp::frame::rtcp_sdes_item item; + uvgrtp::frame::rtcp_sdes_chunk chunk; + read_ssrc(packet, read_ptr, chunk.ssrc); - item.type = packet[ptr++]; - item.length = packet[ptr++]; - item.data = (void*)new uint8_t[item.length]; + // Read chunk items, 2 makes sure we at least get item type and length + while (read_ptr + 2 <= packet_end) + { + uvgrtp::frame::rtcp_sdes_item item; + item.type = packet[read_ptr]; + item.length = packet[read_ptr + 1]; + read_ptr += 2; - memcpy(item.data, &packet[ptr], item.length); - ptr += item.length; // TODO: Clang warning here + if (read_ptr + item.length <= packet_end) + { + item.data = (void*)new uint8_t[item.length]; + + memcpy(item.data, &packet[read_ptr], item.length); + read_ptr += item.length; + } + + chunk.items.push_back(item); + } + frame->chunks.push_back(chunk); } sdes_mutex_.lock(); @@ -1115,37 +1298,41 @@ rtp_error_t uvgrtp::rtcp::handle_sdes_packet(uint8_t* packet, size_t size, } else if (sdes_hook_u_) { sdes_hook_u_(std::unique_ptr(frame)); } else { - /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ - if (participants_[frame->ssrc]->sdes_frame) + + // Deallocate previous frame from the buffer if it exists, it's going to get overwritten + if (participants_[sender_ssrc]->sdes_frame) { - for (auto& item : participants_[frame->ssrc]->sdes_frame->items) + for (auto& chunk : participants_[sender_ssrc]->sdes_frame->chunks) { - delete[](uint8_t*)item.data; + for (auto& item : chunk.items) + { + delete[](uint8_t*)item.data; + item.data = nullptr; + } } - delete participants_[frame->ssrc]->sdes_frame; + delete participants_[sender_ssrc]->sdes_frame; + participants_[sender_ssrc]->sdes_frame = nullptr; } - participants_[frame->ssrc]->sdes_frame = frame; + participants_[sender_ssrc]->sdes_frame = frame; } sdes_mutex_.unlock(); + return RTP_OK; } -rtp_error_t uvgrtp::rtcp::handle_bye_packet(uint8_t* packet, size_t size) +rtp_error_t uvgrtp::rtcp::handle_bye_packet(uint8_t* packet, size_t& read_ptr, + size_t packet_end, uvgrtp::frame::rtcp_header& header) { - if (!packet || !size) + for (size_t i = 0; i < packet_end; i += sizeof(uint32_t)) { - return RTP_INVALID_VALUE; - } - - for (size_t i = 4; i < size; i += sizeof(uint32_t)) - { - uint32_t ssrc = ntohl(*(uint32_t*)&packet[i]); + uint32_t ssrc = 0; + read_ssrc(packet, read_ptr, ssrc); if (!is_participant(ssrc)) { - LOG_WARN("Participants 0x%x is not part of this group!", ssrc); + LOG_WARN("Participant %lu is not part of this group!", ssrc); continue; } @@ -1154,27 +1341,19 @@ rtp_error_t uvgrtp::rtcp::handle_bye_packet(uint8_t* packet, size_t size) participants_.erase(ssrc); } + // TODO: Give BYE packet to user and read optional reason for BYE + + // TODO: Should we exit the whole RTCP here? + return RTP_OK; } -rtp_error_t uvgrtp::rtcp::handle_app_packet(uint8_t* packet, size_t size, - uvgrtp::frame::rtcp_header& header) +rtp_error_t uvgrtp::rtcp::handle_app_packet(uint8_t* packet, size_t& read_ptr, + size_t packet_end, uvgrtp::frame::rtcp_header& header) { - if (!packet || !size) - { - return RTP_INVALID_VALUE; - } - auto frame = new uvgrtp::frame::rtcp_app_packet; frame->header = header; - frame->ssrc = ntohl(*(uint32_t*)&packet[4]); - - auto ret = RTP_OK; - if (srtcp_ && (ret = srtcp_->handle_rtcp_decryption(flags_, frame->ssrc, packet, size)) != RTP_OK) - { - delete frame; - return ret; - } + read_ssrc(packet, read_ptr, frame->ssrc); /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ if (!is_participant(frame->ssrc)) @@ -1183,15 +1362,14 @@ rtp_error_t uvgrtp::rtcp::handle_app_packet(uint8_t* packet, size_t size, add_participant(frame->ssrc); } - uint32_t rtcp_packet_size = (frame->header.length + 1) * sizeof(uint32_t); - uint32_t application_data_size = rtcp_packet_size - - (RTCP_HEADER_SIZE + SSRC_CSRC_SIZE + APP_NAME_SIZE); + // copy app name and application-dependent data from network packet to RTCP structures + memcpy(frame->name, &packet[read_ptr], APP_NAME_SIZE); + read_ptr += APP_NAME_SIZE; + + uint32_t application_data_size = packet_end - read_ptr; // application data is saved to payload frame->payload = new uint8_t[application_data_size]; - - // copy app name and application-dependent data from network packet to RTCP structures - memcpy(frame->name, &packet[RTCP_HEADER_SIZE + SSRC_CSRC_SIZE], APP_NAME_SIZE); memcpy(frame->payload, &packet[RTCP_HEADER_SIZE + SSRC_CSRC_SIZE + APP_NAME_SIZE], application_data_size); @@ -1217,175 +1395,13 @@ rtp_error_t uvgrtp::rtcp::handle_app_packet(uint8_t* packet, size_t size, return RTP_OK; } -rtp_error_t uvgrtp::rtcp::handle_receiver_report_packet(uint8_t* packet, size_t size, - uvgrtp::frame::rtcp_header& header) -{ - if (!packet || !size) - { - return RTP_INVALID_VALUE; - } - - auto frame = new uvgrtp::frame::rtcp_receiver_report; - frame->header = header; - frame->ssrc = ntohl(*(uint32_t*)&packet[RTCP_HEADER_SIZE]); - - auto ret = RTP_OK; - if (srtcp_ && (ret = srtcp_->handle_rtcp_decryption(flags_, frame->ssrc, packet, size)) != RTP_OK) - { - delete frame; - return ret; - } - - /* Receiver Reports are sent from participant that don't send RTP packets - * This means that the sender of this report is not in the participants_ map - * but rather in the initial_participants_ vector - * - * Check if that's the case and if so, move the entry from initial_participants_ to participants_ */ - if (!is_participant(frame->ssrc)) - { - LOG_WARN("Got a Receiver Report from an unknown participant"); - add_participant(frame->ssrc); - } - - if (!frame->header.count) - { - LOG_ERROR("Receiver Report cannot have 0 report blocks!"); - return RTP_INVALID_VALUE; - } - - read_reports(packet, size, frame->header.count, false, frame->report_blocks); - - rr_mutex_.lock(); - if (receiver_hook_) { - receiver_hook_(frame); - } else if (rr_hook_f_) { - rr_hook_f_(std::shared_ptr(frame)); - } else if (rr_hook_u_) { - rr_hook_u_(std::unique_ptr(frame)); - } else { - /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ - if (participants_[frame->ssrc]->rr_frame) - { - delete participants_[frame->ssrc]->rr_frame; - } - - participants_[frame->ssrc]->rr_frame = frame; - } - rr_mutex_.unlock(); - - return RTP_OK; -} - -rtp_error_t uvgrtp::rtcp::handle_sender_report_packet(uint8_t* packet, size_t size, - uvgrtp::frame::rtcp_header& header) -{ - if (!packet || !size) - { - return RTP_INVALID_VALUE; - } - - auto frame = new uvgrtp::frame::rtcp_sender_report; - frame->header = header; - frame->ssrc = ntohl(*(uint32_t*)&packet[4]); - - auto ret = RTP_OK; - if (srtcp_ && (ret = srtcp_->handle_rtcp_decryption(flags_, frame->ssrc, packet, size)) != RTP_OK) - { - delete frame; - return ret; - } - - if (!is_participant(frame->ssrc)) - { - LOG_WARN("Sender Report received from an unknown participant"); - add_participant(frame->ssrc); - } - - frame->sender_info.ntp_msw = ntohl(*(uint32_t*)&packet[8]); - frame->sender_info.ntp_lsw = ntohl(*(uint32_t*)&packet[12]); - frame->sender_info.rtp_ts = ntohl(*(uint32_t*)&packet[16]); - frame->sender_info.pkt_cnt = ntohl(*(uint32_t*)&packet[20]); - frame->sender_info.byte_cnt = ntohl(*(uint32_t*)&packet[24]); - - participants_[frame->ssrc]->stats.sr_ts = uvgrtp::clock::hrc::now(); - participants_[frame->ssrc]->stats.lsr = - ((frame->sender_info.ntp_msw & 0xffff) << 16) | - (frame->sender_info.ntp_lsw >> 16); - - read_reports(packet, size, frame->header.count, true, frame->report_blocks); - - sr_mutex_.lock(); - if (sender_hook_) { - sender_hook_(frame); - } else if (sr_hook_f_) { - sr_hook_f_(std::shared_ptr(frame)); - } else if (sr_hook_u_) { - sr_hook_u_(std::unique_ptr(frame)); - } else { - /* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */ - if (participants_[frame->ssrc]->sr_frame) - { - delete participants_[frame->ssrc]->sr_frame; - } - - participants_[frame->ssrc]->sr_frame = frame; - } - sr_mutex_.unlock(); - - return RTP_OK; -} - -void uvgrtp::rtcp::read_rtcp_header(const uint8_t* packet, uvgrtp::frame::rtcp_header& header) -{ - header.version = (packet[0] >> 6) & 0x3; - header.padding = (packet[0] >> 5) & 0x1; - - header.pkt_type = packet[1] & 0xff; - - if (header.pkt_type == uvgrtp::frame::RTCP_FT_APP) - { - header.pkt_subtype = packet[0] & 0x1f; - } else { - header.count = packet[0] & 0x1f; - } - - header.length = ntohs(*(uint16_t*)&packet[2]); -} - -void uvgrtp::rtcp::read_reports(const uint8_t* packet, size_t size, uint8_t count, bool has_sender_block, - std::vector& reports) -{ - uint32_t report_section = RTCP_HEADER_SIZE + SSRC_CSRC_SIZE; - - if (has_sender_block) - { - report_section += SENDER_INFO_SIZE; - } - - for (int i = 0; i < count; ++i) - { - uint32_t report_position = report_section + (i * REPORT_BLOCK_SIZE); - - if (size >= report_position + REPORT_BLOCK_SIZE) - { - uvgrtp::frame::rtcp_report_block report; - report.ssrc = ntohl(*(uint32_t*)&packet[report_position + 0]); - report.fraction = (ntohl(*(uint32_t*)&packet[report_position + 4])) >> 24; - report.lost = (ntohl(*(int32_t*)&packet[report_position + 4])) & 0xfffffd; - report.last_seq = ntohl(*(uint32_t*)&packet[report_position + 8]); - report.jitter = ntohl(*(uint32_t*)&packet[report_position + 12]); - report.lsr = ntohl(*(uint32_t*)&packet[report_position + 16]); - report.dlsr = ntohl(*(uint32_t*)&packet[report_position + 20]); - - reports.push_back(report); - } else { - LOG_DEBUG("Received rtcp packet is smaller than the indicated number of reports!"); - } - } -} - rtp_error_t uvgrtp::rtcp::send_rtcp_packet_to_participants(uint8_t* frame, size_t frame_size, bool encrypt) { + if (!frame) + { + return RTP_GENERIC_ERROR; + } + rtp_error_t ret = RTP_OK; if (encrypt && srtcp_ && @@ -1413,8 +1429,8 @@ rtp_error_t uvgrtp::rtcp::send_rtcp_packet_to_participants(uint8_t* frame, size_ LOG_ERROR("Tried to send RTCP packet when socket does not exist!"); } } - delete[] frame; + delete[] frame; return ret; } @@ -1431,20 +1447,26 @@ rtp_error_t uvgrtp::rtcp::generate_report() } } - size_t sdes_packet_size = get_sdes_packet_size(ourItems_); + bool add_sdes = false; - size_t compound_packet_size = get_rr_packet_size(flags_, reports) + sdes_packet_size; + size_t compound_packet_size = get_rr_packet_size(flags_, reports); if (our_role_ == SENDER && our_stats.sent_rtp_packet) { - compound_packet_size = get_sr_packet_size(flags_, reports) + sdes_packet_size; + compound_packet_size = get_sr_packet_size(flags_, reports); } + + if (add_sdes) + { + compound_packet_size += get_sdes_packet_size(ourItems_); + } + uint8_t* frame = new uint8_t[compound_packet_size]; memset(frame, 0, compound_packet_size); // see https://datatracker.ietf.org/doc/html/rfc3550#section-6.4.1 - int ptr = 0; + int write_ptr = 0; if (our_role_ == SENDER && our_stats.sent_rtp_packet) { // sender reports have sender information in addition compared to receiver reports @@ -1465,17 +1487,17 @@ 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); - construct_rtcp_header(frame, ptr, sender_report_size, reports, uvgrtp::frame::RTCP_FT_SR); - construct_ssrc(frame, ptr, ssrc_); - construct_sender_info(frame, ptr, ntp_ts, rtp_ts, our_stats.sent_pkts, our_stats.sent_bytes); + 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); our_stats.sent_rtp_packet = false; } else { // RECEIVER size_t receiver_report_size = get_rr_packet_size(flags_, reports); LOG_DEBUG("Generating RTCP Receiver report size: %li", receiver_report_size); - construct_rtcp_header(frame, ptr, receiver_report_size, reports, uvgrtp::frame::RTCP_FT_RR); - construct_ssrc(frame, ptr, ssrc_); + construct_rtcp_header(frame, write_ptr, receiver_report_size, reports, uvgrtp::frame::RTCP_FT_RR); + construct_ssrc(frame, write_ptr, ssrc_); } // the report blocks for sender or receiver report. Both have same reports. @@ -1498,7 +1520,7 @@ rtp_error_t uvgrtp::rtcp::generate_report() dlrs = 0; } - construct_report_block(frame, ptr, p.first, fraction, dropped_packets, + construct_report_block(frame, write_ptr, p.first, fraction, dropped_packets, p.second->stats.cycles, p.second->stats.max_seq, p.second->stats.jitter, p.second->stats.lsr, dlrs); @@ -1507,18 +1529,20 @@ rtp_error_t uvgrtp::rtcp::generate_report() } } - // header construction also adds our ssrc - if (!construct_rtcp_header(frame, ptr, sdes_packet_size, num_receivers_, - uvgrtp::frame::RTCP_FT_SDES) || - !construct_ssrc(frame, ptr, ssrc_) || - !construct_sdes_items(frame, ptr, ourItems_)) + if (add_sdes) { - delete[] frame; - return RTP_GENERIC_ERROR; - } + // add the SDES packet after the SR/RR + 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_ })) + { + delete[] frame; + return RTP_GENERIC_ERROR; + } - LOG_DEBUG("Sending RTCP report compound packet, Total size: %li, SDES packet size: %li", - compound_packet_size, sdes_packet_size); + LOG_DEBUG("Sending RTCP report compound packet, Total size: %lli, SDES packet size: %lli", + compound_packet_size, get_sdes_packet_size(ourItems_)); + } return send_rtcp_packet_to_participants(frame, compound_packet_size, true); } @@ -1535,17 +1559,17 @@ rtp_error_t uvgrtp::rtcp::send_sdes_packet(const std::vector ssrcs) memset(frame, 0, rtcp_packet_size); rtp_error_t ret = RTP_OK; - int ptr = 0; + int write_ptr = 0; uint16_t secondField = (ssrcs.size() & 0x1f); // header construction does not add our ssrc for BYE - if (!construct_rtcp_header(frame, ptr, rtcp_packet_size, secondField, + if (!construct_rtcp_header(frame, write_ptr, rtcp_packet_size, secondField, uvgrtp::frame::RTCP_FT_BYE) || - !construct_ssrc(frame, ptr, ssrc_) || - !construct_bye_packet(frame, ptr, ssrcs)) + !construct_bye_packet(frame, write_ptr, ssrcs)) { delete[] frame; return RTP_GENERIC_ERROR; } - // TODO: Enable encryption because bye actually has ssrc + // TODO: Should only be sent in a compound packet return send_rtcp_packet_to_participants(frame, rtcp_packet_size, false); } @@ -1585,18 +1608,19 @@ rtp_error_t uvgrtp::rtcp::send_app_packet(const char* name, uint8_t subtype, uint8_t* frame = new uint8_t[rtcp_packet_size]; memset(frame, 0, rtcp_packet_size); - int ptr = 0; + int write_ptr = 0; uint16_t secondField = (subtype & 0x1f); - if (!construct_rtcp_header(frame, ptr, rtcp_packet_size, secondField, + if (!construct_rtcp_header(frame, write_ptr, rtcp_packet_size, secondField, uvgrtp::frame::RTCP_FT_APP) || - !construct_ssrc(frame, ptr, ssrc_) || - !construct_app_packet(frame, ptr, name, payload, payload_len)) + !construct_ssrc(frame, write_ptr, ssrc_) || + !construct_app_packet(frame, write_ptr, name, payload, payload_len)) { delete[] frame; return RTP_GENERIC_ERROR; } + // TODO: Should only be sent in compound packet return send_rtcp_packet_to_participants(frame, rtcp_packet_size, true); } diff --git a/src/rtcp_packets.cc b/src/rtcp_packets.cc index b78b093..45604f1 100644 --- a/src/rtcp_packets.cc +++ b/src/rtcp_packets.cc @@ -115,12 +115,13 @@ bool uvgrtp::construct_app_packet(uint8_t* frame, int& ptr, } bool uvgrtp::construct_sdes_chunk(uint8_t* frame, int& ptr, - uint32_t ssrc, const std::vector& items) { + uvgrtp::frame::rtcp_sdes_chunk chunk) { + bool have_cname = false; - construct_ssrc(frame, ptr, ssrc); + construct_ssrc(frame, ptr, chunk.ssrc); - for (auto& item : items) + for (auto& item : chunk.items) { if (item.length <= 255) { @@ -136,6 +137,11 @@ bool uvgrtp::construct_sdes_chunk(uint8_t* frame, int& ptr, } } + if (!have_cname) + { + LOG_ERROR("SDES chunk did not contain cname!"); + } + return have_cname; } diff --git a/src/rtcp_packets.hh b/src/rtcp_packets.hh index 3f5a0f1..46bd31c 100644 --- a/src/rtcp_packets.hh +++ b/src/rtcp_packets.hh @@ -36,8 +36,7 @@ namespace uvgrtp uint32_t lsr, uint32_t dlsr); // Add the items to the frame - bool construct_sdes_chunk(uint8_t* frame, int& ptr, - uint32_t ssrc, const std::vector& items); + bool construct_sdes_chunk(uint8_t* frame, int& ptr, uvgrtp::frame::rtcp_sdes_chunk chunk); // Add the name and payload to APP packet, remember to also add SSRC separately bool construct_app_packet(uint8_t* frame, int& ptr,