Fix RTCP APP generation/reception code

Operate on a raw block of memory, extract interesting fields from the
packet and create user-friendly struct for the APP packet that
can be queried using SSRC

Caller must remember to free the memory occupied by the APP payload
This commit is contained in:
Aaro Altonen 2020-08-26 15:32:51 +03:00
parent 2807ccc83b
commit e586a007be
4 changed files with 69 additions and 128 deletions

View File

@ -84,9 +84,12 @@ namespace uvg_rtp {
};
PACKED_STRUCT(rtcp_header) {
uint8_t version:2;
uint8_t padding:1;
uint8_t count:5;
uint8_t version;
uint8_t padding;
union {
uint8_t count;
uint8_t pkt_subtype; /* for app packets */
};
uint8_t pkt_type;
uint16_t length;
};
@ -147,23 +150,18 @@ namespace uvg_rtp {
std::vector<rtcp_sdes_item> items;
};
struct rtcp_app_packet {
struct rtcp_header header;
uint32_t ssrc;
uint8_t name[4];
uint8_t *payload;
};
PACKED_STRUCT(rtcp_bye_frame) {
struct rtcp_header header;
uint32_t ssrc[1];
};
PACKED_STRUCT(rtcp_app_frame) {
uint8_t version:2;
uint8_t padding:1;
uint8_t pkt_subtype:5;
uint8_t pkt_type;
uint16_t length;
uint32_t ssrc;
uint8_t name[4];
uint8_t payload[1];
};
PACKED_STRUCT(zrtp_frame) {
uint8_t version:4;
uint16_t unused:12;
@ -204,8 +202,6 @@ namespace uvg_rtp {
* Return nullptr on error and set rtp_errno to:
* RTP_MEMORY_ERROR if allocation of memory failed
* RTP_INVALID_VALUE if one of the parameters was invalid */
rtcp_app_frame *alloc_rtcp_app_frame(std::string name, uint8_t subtype, size_t payload_len);
rtcp_sdes_frame *alloc_rtcp_sdes_frame(size_t ssrc_count, size_t total_len);
rtcp_receiver_frame *alloc_rtcp_receiver_frame(size_t nblocks);
rtcp_sender_frame *alloc_rtcp_sender_frame(size_t nblocks);
rtcp_bye_frame *alloc_rtcp_bye_frame(size_t ssrc_count);
@ -228,8 +224,6 @@ namespace uvg_rtp {
* Return RTP_INVALID_VALUE if "frame" is nullptr */
rtp_error_t dealloc_frame(rtcp_sender_frame *frame);
rtp_error_t dealloc_frame(rtcp_receiver_frame *frame);
rtp_error_t dealloc_frame(rtcp_sdes_frame *frame);
rtp_error_t dealloc_frame(rtcp_bye_frame *frame);
rtp_error_t dealloc_frame(rtcp_app_frame *frame);
};
};

View File

@ -67,7 +67,7 @@ namespace uvg_rtp {
uvg_rtp::frame::rtcp_sender_report *s_frame;
uvg_rtp::frame::rtcp_receiver_report *r_frame;
uvg_rtp::frame::rtcp_sdes_packet *sdes_frame;
uvg_rtp::frame::rtcp_app_frame *app_frame;
uvg_rtp::frame::rtcp_app_packet *app_frame;
};
class rtcp : public runner {
@ -118,8 +118,8 @@ namespace uvg_rtp {
* Return RTP_INVALID_VALUE if "frame" is in some way invalid
* Return RTP_SEND_ERROR if sending "frame" did not succeed (see socket.hh for details) */
rtp_error_t send_sdes_packet(std::vector<uvg_rtp::frame::rtcp_sdes_item>& items);
rtp_error_t send_app_packet(char *name, uint8_t subtype, size_t payload_len, uint8_t *payload);
rtp_error_t send_bye_packet(uvg_rtp::frame::rtcp_bye_frame *frame);
rtp_error_t send_app_packet(uvg_rtp::frame::rtcp_app_frame *frame);
/* Return the latest RTCP packet received from participant of "ssrc"
* Return nullptr if we haven't received this kind of packet or if "ssrc" doesn't exist
@ -128,7 +128,7 @@ namespace uvg_rtp {
uvg_rtp::frame::rtcp_sender_report *get_sender_packet(uint32_t ssrc);
uvg_rtp::frame::rtcp_receiver_report *get_receiver_packet(uint32_t ssrc);
uvg_rtp::frame::rtcp_sdes_packet *get_sdes_packet(uint32_t ssrc);
uvg_rtp::frame::rtcp_app_frame *get_app_packet(uint32_t ssrc);
uvg_rtp::frame::rtcp_app_packet *get_app_packet(uint32_t ssrc);
/* create RTCP BYE packet using our own SSRC and send it to all participants */
rtp_error_t terminate_self();
@ -189,7 +189,7 @@ namespace uvg_rtp {
rtp_error_t install_sender_hook(void (*hook)(uvg_rtp::frame::rtcp_sender_report *));
rtp_error_t install_receiver_hook(void (*hook)(uvg_rtp::frame::rtcp_receiver_report *));
rtp_error_t install_sdes_hook(void (*hook)(uvg_rtp::frame::rtcp_sdes_packet *));
rtp_error_t install_app_hook(void (*hook)(uvg_rtp::frame::rtcp_app_frame *));
rtp_error_t install_app_hook(void (*hook)(uvg_rtp::frame::rtcp_app_packet *));
/* Update RTCP-related sender statistics */
rtp_error_t update_sender_stats(size_t pkt_size);
@ -324,6 +324,6 @@ namespace uvg_rtp {
void (*sender_hook_)(uvg_rtp::frame::rtcp_sender_report *);
void (*receiver_hook_)(uvg_rtp::frame::rtcp_receiver_report *);
void (*sdes_hook_)(uvg_rtp::frame::rtcp_sdes_packet *);
void (*app_hook_)(uvg_rtp::frame::rtcp_app_frame *);
void (*app_hook_)(uvg_rtp::frame::rtcp_app_packet *);
};
};

View File

@ -134,36 +134,6 @@ uvg_rtp::frame::rtcp_sender_frame *uvg_rtp::frame::alloc_rtcp_sender_frame(size_
return frame;
}
uvg_rtp::frame::rtcp_sdes_frame *uvg_rtp::frame::alloc_rtcp_sdes_frame(size_t ssrc_count, size_t total_len)
{
if (total_len == 0) {
LOG_ERROR("Cannot allocate empty SDES packet!");
rtp_errno = RTP_INVALID_VALUE;
return nullptr;
}
size_t total_size = sizeof(rtcp_header) + total_len;
auto *frame = (uvg_rtp::frame::rtcp_sdes_frame *)new uint8_t[total_size];
if (!frame) {
LOG_ERROR("Failed to allocate memory for RTCP sender report");
rtp_errno = RTP_MEMORY_ERROR;
return nullptr;
}
frame->header.version = 2;
frame->header.padding = 0;
frame->header.pkt_type = uvg_rtp::frame::RTCP_FT_SDES;
frame->header.length = total_size;
frame->header.count = ssrc_count;
/* caller fills these */
memset(frame->items, 0, total_len);
return frame;
}
uvg_rtp::frame::rtcp_bye_frame *uvg_rtp::frame::alloc_rtcp_bye_frame(size_t ssrc_count)
{
if (ssrc_count == 0) {
@ -190,31 +160,6 @@ uvg_rtp::frame::rtcp_bye_frame *uvg_rtp::frame::alloc_rtcp_bye_frame(size_t ssrc
return frame;
}
uvg_rtp::frame::rtcp_app_frame *uvg_rtp::frame::alloc_rtcp_app_frame(std::string name, uint8_t subtype, size_t payload_len)
{
if (name == "" || payload_len == 0) {
rtp_errno = RTP_INVALID_VALUE;
return nullptr;
}
size_t total_size = sizeof(rtcp_app_frame) + payload_len;
auto *frame = (uvg_rtp::frame::rtcp_app_frame *)new uint8_t[total_size];
if (!frame) {
LOG_ERROR("Failed to allocate memory for RTCP sender report");
rtp_errno = RTP_MEMORY_ERROR;
return nullptr;
}
frame->version = 2;
frame->padding = 0;
frame->pkt_type = uvg_rtp::frame::RTCP_FT_APP;
frame->pkt_subtype = subtype;
frame->length = total_size;
return frame;
}
rtp_error_t uvg_rtp::frame::dealloc_frame(uvg_rtp::frame::rtcp_sender_frame *frame)
{
if (!frame)
@ -224,15 +169,6 @@ rtp_error_t uvg_rtp::frame::dealloc_frame(uvg_rtp::frame::rtcp_sender_frame *fra
return RTP_OK;
}
rtp_error_t uvg_rtp::frame::dealloc_frame(rtcp_sdes_frame *frame)
{
if (!frame)
return RTP_INVALID_VALUE;
delete[] frame;
return RTP_OK;
}
rtp_error_t uvg_rtp::frame::dealloc_frame(rtcp_bye_frame *frame)
{
if (!frame)
@ -241,12 +177,3 @@ rtp_error_t uvg_rtp::frame::dealloc_frame(rtcp_bye_frame *frame)
delete[] frame;
return RTP_OK;
}
rtp_error_t uvg_rtp::frame::dealloc_frame(rtcp_app_frame *frame)
{
if (!frame)
return RTP_INVALID_VALUE;
delete[] frame;
return RTP_OK;
}

View File

@ -4,7 +4,7 @@
#include "rtcp.hh"
uvg_rtp::frame::rtcp_app_frame *uvg_rtp::rtcp::get_app_packet(uint32_t ssrc)
uvg_rtp::frame::rtcp_app_packet *uvg_rtp::rtcp::get_app_packet(uint32_t ssrc)
{
if (participants_.find(ssrc) == participants_.end())
return nullptr;
@ -15,7 +15,7 @@ uvg_rtp::frame::rtcp_app_frame *uvg_rtp::rtcp::get_app_packet(uint32_t ssrc)
return frame;
}
rtp_error_t uvg_rtp::rtcp::install_app_hook(void (*hook)(uvg_rtp::frame::rtcp_app_frame *))
rtp_error_t uvg_rtp::rtcp::install_app_hook(void (*hook)(uvg_rtp::frame::rtcp_app_packet *))
{
if (!hook)
return RTP_INVALID_VALUE;
@ -24,52 +24,72 @@ rtp_error_t uvg_rtp::rtcp::install_app_hook(void (*hook)(uvg_rtp::frame::rtcp_ap
return RTP_OK;
}
rtp_error_t uvg_rtp::rtcp::handle_app_packet(uint8_t *frame, size_t size)
rtp_error_t uvg_rtp::rtcp::handle_app_packet(uint8_t *packet, size_t size)
{
#if 0
if (!frame)
if (!packet || !size)
return RTP_INVALID_VALUE;
frame->ssrc = ntohl(frame->ssrc);
frame->length = ntohs(frame->length);
auto frame = new uvg_rtp::frame::rtcp_app_packet;
/* We need to make a copy of the frame because right now frame points to RTCP recv buffer
* Deallocate previous frame if it exists */
if (participants_[frame->ssrc]->app_frame != nullptr)
(void)uvg_rtp::frame::dealloc_frame(participants_[frame->ssrc]->app_frame);
frame->header.version = (packet[0] >> 6) & 0x3;
frame->header.padding = (packet[0] >> 5) & 0x1;
frame->header.pkt_subtype = packet[0] & 0x1f;
frame->header.length = ntohs(*(uint16_t *)&packet[2]);
frame->ssrc = ntohl(*(uint32_t *)&packet[4]);
uint8_t *cpy_frame = new uint8_t[size];
memcpy(cpy_frame, frame, size);
/* Deallocate previous frame from the buffer if it exists, it's going to get overwritten */
if (participants_[frame->ssrc]->app_frame) {
delete[] participants_[frame->ssrc]->app_frame->payload;
delete participants_[frame->ssrc]->app_frame;
}
memcpy(frame->name, &packet[ 8], 4);
memcpy(frame->payload, &packet[12], frame->header.length - 12);
if (app_hook_)
app_hook_((uvg_rtp::frame::rtcp_app_frame *)cpy_frame);
app_hook_(frame);
else
participants_[frame->ssrc]->app_frame = (uvg_rtp::frame::rtcp_app_frame *)cpy_frame;
participants_[frame->ssrc]->app_frame = frame;
#endif
return RTP_OK;
}
rtp_error_t uvg_rtp::rtcp::send_app_packet(uvg_rtp::frame::rtcp_app_frame *frame)
rtp_error_t uvg_rtp::rtcp::send_app_packet(char *name, uint8_t subtype, size_t payload_len, uint8_t *payload)
{
if (!frame)
return RTP_INVALID_VALUE;
size_t frame_size;
rtp_error_t ret;
uint8_t *frame;
uint16_t len = frame->length;
uint32_t ssrc = frame->ssrc;
frame_size = 4; /* rtcp header */
frame_size += 4; /* our ssrc */
frame_size += 4; /* name */
frame_size += payload_len;
frame->length = htons(frame->length);
frame->ssrc = htonl(frame->ssrc);
if (!(frame = new uint8_t[frame_size])) {
LOG_ERROR("Failed to allocate space for RTCP Receiver Report");
return RTP_MEMORY_ERROR;
}
memset(frame, 0, frame_size);
if (!is_participant(ssrc)) {
LOG_ERROR("Unknown participant 0x%x", ssrc);
return RTP_INVALID_VALUE;
frame[0] = (2 << 6) | (0 << 5) | (subtype & 0x1f);
frame[1] = uvg_rtp::frame::RTCP_FT_APP;
*(uint16_t *)&frame[2] = htons(frame_size);
*(uint32_t *)&frame[4] = htonl(ssrc_);
memcpy(&frame[ 8], name, 4);
memcpy(&frame[12], payload, payload_len);
for (auto& p : participants_) {
if ((ret = p.second->socket->sendto(p.second->address, frame, frame_size, 0)) != RTP_OK) {
LOG_ERROR("sendto() failed!");
goto end;
}
update_rtcp_bandwidth(frame_size);
}
rtp_error_t ret = participants_[ssrc]->socket->sendto((uint8_t *)frame, len, 0, NULL);
if (ret == RTP_OK)
update_rtcp_bandwidth(len);
end:
delete[] frame;
return ret;
}