Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for transport-wide CC on outgoing streams #1889

Merged
merged 17 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -3508,7 +3508,7 @@ static gint rtcp_transport_wide_cc_stats_comparator(gconstpointer item1, gconstp
static gboolean janus_ice_outgoing_transport_wide_cc_feedback(gpointer user_data) {
janus_ice_handle *handle = (janus_ice_handle *)user_data;
janus_ice_stream *stream = handle->stream;
if(stream && stream->do_transport_wide_cc) {
if(stream && stream->video_send && stream->do_transport_wide_cc) {
/* Create a transport wide feedback message */
size_t size = 1300;
char rtcpbuf[1300];
Expand Down Expand Up @@ -4096,6 +4096,14 @@ static gboolean janus_ice_outgoing_traffic_handle(janus_ice_handle *handle, janu
/* ... but only if this isn't a retransmission (for those we already set it before) */
header->ssrc = htonl(video ? stream->video_ssrc : stream->audio_ssrc);
}
/* Set the transport-wide sequence number, if needed */
if(video && stream->transport_wide_cc_ext_id > 0) {
stream->transport_wide_cc_out_seq_num++;
if(janus_rtp_header_extension_set_transport_wide_cc(pkt->data, pkt->length,
stream->transport_wide_cc_ext_id, stream->transport_wide_cc_out_seq_num) < 0) {
JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting transport wide CC sequence number...\n", handle->handle_id);
}
}
/* Keep track of payload types too */
if(!video && stream->audio_payload_type < 0) {
stream->audio_payload_type = header->type;
Expand Down Expand Up @@ -4374,6 +4382,14 @@ void janus_ice_relay_rtp(janus_ice_handle *handle, janus_plugin_rtp *packet) {
extheader->length = 0;
/* Iterate on all extensions we need */
char *index = extensions + 4;
/* Check if we need to add the transport-wide CC extension */
if(packet->video && handle->stream->transport_wide_cc_ext_id > 0) {
*index = (handle->stream->transport_wide_cc_ext_id << 4) + 1;
/* We'll actually set the sequence number later, when sending the packet */
memset(index+1, 0, 2);
index += 3;
extlen += 3;
}
/* Check if we need to add the mid extension */
if(handle->stream->mid_ext_id > 0) {
char *mid = packet->video ? handle->video_mid : handle->audio_mid;
Expand Down
2 changes: 2 additions & 0 deletions ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ struct janus_ice_stream {
gboolean do_transport_wide_cc;
/*! \brief Transport wide cc rtp ext ID */
gint transport_wide_cc_ext_id;
/*! \brief Last sent transport wide seq num */
guint16 transport_wide_cc_out_seq_num;
/*! \brief Last received transport wide seq num */
guint32 transport_wide_cc_last_seq_num;
/*! \brief Last transport wide seq num sent on feedback */
Expand Down
8 changes: 7 additions & 1 deletion janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -3378,7 +3378,7 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
}
}
/* Make sure we don't send the rid/repaired-rid attributes when offering ourselves */
int mid_ext_id = 0, audiolevel_ext_id = 0, videoorientation_ext_id = 0;
int mid_ext_id = 0, transport_wide_cc_ext_id = 0, audiolevel_ext_id = 0, videoorientation_ext_id = 0;
GList *temp = parsed_sdp->m_lines;
while(temp) {
janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
Expand All @@ -3388,6 +3388,8 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
if(a->name && a->value) {
if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
mid_ext_id = atoi(a->value);
else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
transport_wide_cc_ext_id = atoi(a->value);
else if(strstr(a->value, JANUS_RTP_EXTMAP_AUDIO_LEVEL))
audiolevel_ext_id = atoi(a->value);
else if(strstr(a->value, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
Expand All @@ -3406,6 +3408,10 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
}
if(ice_handle->stream && ice_handle->stream->mid_ext_id != mid_ext_id)
ice_handle->stream->mid_ext_id = mid_ext_id;
if(ice_handle->stream && ice_handle->stream->transport_wide_cc_ext_id != transport_wide_cc_ext_id) {
ice_handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
Copy link
Contributor

@tmatth tmatth Dec 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this could be simplified to transport_wide_cc_ext_id > 0;

ice_handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
}
if(ice_handle->stream && ice_handle->stream->audiolevel_ext_id != audiolevel_ext_id)
ice_handle->stream->audiolevel_ext_id = audiolevel_ext_id;
if(ice_handle->stream && ice_handle->stream->videoorientation_ext_id != videoorientation_ext_id)
Expand Down
165 changes: 139 additions & 26 deletions rtcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@
#include "rtcp.h"
#include "utils.h"


/* Transport CC statuses */
typedef enum janus_rtp_packet_status {
janus_rtp_packet_status_notreceived = 0,
janus_rtp_packet_status_smalldelta = 1,
janus_rtp_packet_status_largeornegativedelta = 2,
janus_rtp_packet_status_reserved = 3
} janus_rtp_packet_status;
static const char *janus_rtp_packet_status_description(janus_rtp_packet_status status) {
switch(status) {
case janus_rtp_packet_status_notreceived: return "notreceived";
case janus_rtp_packet_status_smalldelta: return "smalldelta";
case janus_rtp_packet_status_largeornegativedelta: return "largeornegativedelta";
case janus_rtp_packet_status_reserved: return "reserved";
default: break;
}
return NULL;
}


gboolean janus_is_rtcp(char *buf, guint len) {
if (len < 8)
return FALSE;
Expand Down Expand Up @@ -134,7 +154,7 @@ guint32 janus_rtcp_get_receiver_ssrc(char *packet, int len) {
return 0;
}

/* Helper to handle an incoming SR: triggered by a call to janus_rtcp_fix_ssrc with fixssrc=0 */
/* Helper to handle an incoming SR: triggered by a call to janus_rtcp_fix_ssrc with a valid context pointer */
static void janus_rtcp_incoming_sr(janus_rtcp_context *ctx, janus_rtcp_sr *sr) {
if(ctx == NULL)
return;
Expand All @@ -146,6 +166,103 @@ static void janus_rtcp_incoming_sr(janus_rtcp_context *ctx, janus_rtcp_sr *sr) {
ctx->lsr = (ntp >> 16);
}

/* Helper to handle an incoming transport-cc feedback: triggered by a call to janus_rtcp_fix_ssrc a valid context pointer */
static void janus_rtcp_incoming_transport_cc(janus_rtcp_context *ctx, janus_rtcp_fb *twcc, int total) {
if(ctx == NULL || twcc == NULL || total < 16)
return;
/* Parse the header first */
uint8_t *data = (uint8_t *)twcc->fci;
uint16_t base_seq = 0, status_count = 0;
uint32_t reference = 0;
uint8_t fb_pkt = 0;
memcpy(&base_seq, data, sizeof(uint16_t));
base_seq = ntohs(base_seq);
memcpy(&status_count, data+2, sizeof(uint16_t));
status_count = ntohs(status_count);
memcpy(&reference, data+4, sizeof(uint32_t));
reference = ntohl(reference) >> 8;
fb_pkt = *(data+7);
JANUS_LOG(LOG_HUGE, "[TWCC] seq=%"SCNu16", psc=%"SCNu16", ref=%"SCNu32", fbpc=%"SCNu8"\n",
base_seq, status_count, reference, fb_pkt);
/* Now traverse the feedback: packet chunks first, and then recv deltas */
total -= 16;
data += 8;
int psc = status_count;
uint16_t chunk = 0;
uint8_t t = 0, ss = 0, s = 0, length = 0;
/* Iterate on all packet chunks */
JANUS_LOG(LOG_HUGE, "[TWCC] Chunks:\n");
int num = 0;
GList *list = NULL;
while(psc > 0 && total > 0) {
num++;
memcpy(&chunk, data, sizeof(uint16_t));
chunk = ntohs(chunk);
t = (chunk & 0x8000) >> 15;
if(t == 0) {
/* Run length */
s = (chunk & 0x6000) >> 13;
length = (chunk & 0x1FFF);
JANUS_LOG(LOG_HUGE, " [%d] t=run-length, s=%s, l=%"SCNu16"\n", num,
janus_rtp_packet_status_description(s), length);
while(length > 0 && psc > 0) {
list = g_list_append(list, GUINT_TO_POINTER(s));
length--;
psc--;
}
} else {
/* Status vector */
ss = (chunk & 0x4000) >> 14;
length = (s ? 7 : 14);
JANUS_LOG(LOG_HUGE, " [%d] t=status-vector, ss=%s, l=%"SCNu16"\n", num,
s ? "2-bit" : "bit", length);
while(length > 0 && psc > 0) {
if(!ss)
s = (chunk & (1 << (length-1))) ? janus_rtp_packet_status_smalldelta : janus_rtp_packet_status_notreceived;
else
s = (chunk & (3 << (2*length-2))) >> (2*length-2);
list = g_list_append(list, GUINT_TO_POINTER(s));
length--;
psc--;
}
}
total -= 2;
data += 2;
}
/* Iterate on all recv deltas */
JANUS_LOG(LOG_HUGE, "[TWCC] Recv Deltas (%d/%"SCNu16"):\n", g_list_length(list), status_count);
num = 0;
uint16_t delta = 0;
uint32_t delta_us = 0;
GList *iter = list;
while(iter != NULL && total > 0) {
num++;
delta = 0;
s = GPOINTER_TO_UINT(iter->data);
if(s == janus_rtp_packet_status_smalldelta) {
/* Small delta = 1 byte */
delta = *data;
total--;
data++;
} else if(s == janus_rtp_packet_status_largeornegativedelta) {
/* Large or negative delta = 2 bytes */
if(total < 2)
break;
memcpy(&delta, data, sizeof(uint16_t));
delta = ntohs(delta);
total -= 2;
data += 2;
}
delta_us = delta*250;
/* Print summary */
JANUS_LOG(LOG_HUGE, " [%02d][%"SCNu16"] %s (%"SCNu32"us)\n", num, base_seq+num-1,
janus_rtp_packet_status_description(s), delta_us);
iter = iter->next;
}
/* TODO Update the context with the feedback we got */
g_list_free(list);
}

/* Link quality estimate filter coefficient */
#define LINK_QUALITY_FILTER_K 3.0

Expand Down Expand Up @@ -438,6 +555,9 @@ int janus_rtcp_fix_ssrc(janus_rtcp_context *ctx, char *packet, int len, int fixs
uint32_t *ssrc = (uint32_t *)rtcpfb->fci;
*ssrc = htonl(newssrcr);
}
} else if(fmt == 15) { /* transport-cc */
/* If an RTCP context was provided, parse this transport-cc feedback */
janus_rtcp_incoming_transport_cc(ctx, rtcpfb, total);
} else {
JANUS_LOG(LOG_HUGE, " #%d ??? -- RTPFB (205, fmt=%d)\n", pno, fmt);
}
Expand Down Expand Up @@ -1368,13 +1488,6 @@ int janus_rtcp_nacks(char *packet, int len, GSList *nacks) {
return words*4+4;
}

typedef enum janus_rtp_packet_status {
janus_rtp_packet_status_notreceived = 0,
janus_rtp_packet_status_smalldelta = 1,
janus_rtp_packet_status_largeornegativedelta = 2,
janus_rtp_packet_status_reserved = 3
} janus_rtp_packet_status;

int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssrc, guint32 media, guint8 feedback_packet_count, GQueue *transport_wide_cc_stats) {
if(packet == NULL || size < sizeof(janus_rtcp_header) || transport_wide_cc_stats == NULL || g_queue_is_empty(transport_wide_cc_stats))
return -1;
Expand All @@ -1401,11 +1514,11 @@ int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssr
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| base sequence number | packet status count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| reference time | fb pkt. count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| base sequence number | packet status count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| reference time | fb pkt. count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

/* The packet as unsigned */
Expand Down Expand Up @@ -1473,7 +1586,7 @@ int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssr
timestamp = stat->timestamp;
}

/* Check if all previoues ones were equal and this one the firt different */
/* Check if all previoues ones were equal and this one the first different */
if (all_same && last_status!=janus_rtp_packet_status_reserved && status!=last_status) {
/* How big was the same run */
if (g_queue_get_length(statuses)>7) {
Expand All @@ -1482,9 +1595,9 @@ int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssr
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T| S | Run Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T| S | Run Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 0
*/
word = janus_push_bits(word, 1, 0);
Expand Down Expand Up @@ -1516,17 +1629,17 @@ int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssr
/* Store las status */
last_status = status;

/* Check if we can still be enquing for a run */
/* Check if we can still be enqueuing for a run */
if (!all_same) {
/* Check */
if (!all_same && max_status==janus_rtp_packet_status_largeornegativedelta && g_queue_get_length(statuses)>6) {
guint32 word = 0;
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| Symbols |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| Symbols |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 1
S = 1
*/
Expand Down Expand Up @@ -1570,11 +1683,11 @@ int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssr
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| symbol list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 1
S = 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| symbol list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 1
S = 0
*/
word = janus_push_bits(word, 1, 1);
word = janus_push_bits(word, 1, 0);
Expand Down
3 changes: 3 additions & 0 deletions rtcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ typedef struct rtcp_context
double in_media_link_quality;
double out_link_quality;
double out_media_link_quality;

/* TODO Incoming transport-wide CC feedback*/

} rtcp_context;
typedef rtcp_context janus_rtcp_context;

Expand Down
17 changes: 15 additions & 2 deletions rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,27 @@ int janus_rtp_header_extension_parse_transport_wide_cc(char *buf, int len, int i
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if (val_len < 2 || val_len > len-(ext-buf)-1) {
if (val_len < 2 || val_len > len-(ext-buf)-1)
return -3;
}
memcpy(transSeqNum, ext+1, sizeof(uint16_t));
*transSeqNum = ntohs(*transSeqNum);
return 0;
}

int janus_rtp_header_extension_set_transport_wide_cc(char *buf, int len, int id, uint16_t transSeqNum) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if (val_len < 2 || val_len > len-(ext-buf)-1)
return -3;
transSeqNum = htons(transSeqNum);
memcpy(ext+1, &transSeqNum, sizeof(uint16_t));
return 0;
}

int janus_rtp_header_extension_replace_id(char *buf, int len, int id, int new_id) {
if(!buf || len < 12)
return -1;
Expand Down
13 changes: 10 additions & 3 deletions rtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,17 @@ int janus_rtp_header_extension_parse_framemarking(char *buf, int len, int id, ja
* @param[in] buf The packet data
* @param[in] len The packet data length in bytes
* @param[in] id The extension ID to look for
* @param[out] transSeqNum transport wide sequence number
* @param[out] transSeqNum Variable to read the transport wide sequence number in
* @returns 0 if found, -1 otherwise */
int janus_rtp_header_extension_parse_transport_wide_cc(char *buf, int len, int id,
uint16_t *transSeqNum);
int janus_rtp_header_extension_parse_transport_wide_cc(char *buf, int len, int id, uint16_t *transSeqNum);

/*! \brief Helper to set a transport wide sequence number (https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01)
* @param[in] buf The packet data
* @param[in] len The packet data length in bytes
* @param[in] id The extension ID to look for
* @param[out] transSeqNum Transport wide sequence number to set
* @returns 0 if found, -1 otherwise */
int janus_rtp_header_extension_set_transport_wide_cc(char *buf, int len, int id, uint16_t transSeqNum);

/*! \brief Helper to replace the ID of an RTP extension with a different one (e.g.,
* to turn a repaired-rtp-stream-id into a rtp-stream-id after a successful rtx)
Expand Down
Loading