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 abs-send-time RTP extension #2721

Merged
merged 5 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions fuzzers/rtp_fuzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
janus_rtp_header_extension_parse_rid((char *)data, size, 1, sdes_item, sizeof(sdes_item));
janus_rtp_header_extension_parse_mid((char *)data, size, 1, sdes_item, sizeof(sdes_item));
janus_rtp_header_extension_parse_transport_wide_cc((char *)data, size, 1, &transport_seq_num);
janus_rtp_header_extension_parse_abs_sent_time((char *)data, size, 1, NULL);

/* Extract codec payload */
int plen = 0;
Expand Down
26 changes: 25 additions & 1 deletion ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -4484,6 +4484,21 @@ 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 abs-send-time value, if needed */
if(video && stream->abs_send_time_ext_id > 0) {
struct timeval tv;
gettimeofday(&tv, NULL);
uint64_t s = tv.tv_sec + 2208988800u;
uint32_t u = tv.tv_usec;
uint32_t f = (u << 12) + (u << 8) - ((u * 3650) >> 6);
uint64_t ntp_ts = (s << 32) + f;
uint64_t abs24 = (ntp_ts >> 14) & 0x00ffffff;
uint32_t abs_ts = abs24;
if(janus_rtp_header_extension_set_abs_send_time(pkt->data, pkt->length,
stream->abs_send_time_ext_id, abs_ts) < 0) {
JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting abs-send-time value...\n", handle->handle_id);
Copy link
Contributor

Choose a reason for hiding this comment

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

imo the documentation on webrtc is misleading here. It says something about the NTP timestamp but this is not what libwebrtc does:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/rtp_rtcp/source/rtp_sender_egress.cc;l=226;drc=6a0a55907bf29c7d6bfedb0f8aa4405ed3e2dcf5
It takes the current monotonic clock and then does some shifting with it:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h;l=42;drc=1f5325b50d27ed221d4a26530908c4b68b834507

i'd strongly recommend the same. Mistakes in this are subtle but may lead to overshooting the available bandwidth

Copy link
Member Author

Choose a reason for hiding this comment

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

Ack, thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

Should be fixed now 👍

}
}
/* Set the transport-wide sequence number, if needed */
if(video && stream->transport_wide_cc_ext_id > 0) {
stream->transport_wide_cc_out_seq_num++;
Expand Down Expand Up @@ -4774,7 +4789,8 @@ void janus_ice_relay_rtp(janus_ice_handle *handle, janus_plugin_rtp *packet) {
int origext = header->extension;
header->extension = 0;
/* Add core and plugin extensions, if any */
if((packet->video && handle->stream->transport_wide_cc_ext_id > 0) || handle->stream->mid_ext_id > 0 ||
if(handle->stream->mid_ext_id > 0 || (packet->video && handle->stream->abs_send_time_ext_id > 0) ||
(packet->video && handle->stream->transport_wide_cc_ext_id > 0) ||
(!packet->video && packet->extensions.audio_level != -1 && handle->stream->audiolevel_ext_id > 0) ||
(packet->video && packet->extensions.video_rotation != -1 && handle->stream->videoorientation_ext_id > 0)) {
header->extension = 1;
Expand All @@ -4784,6 +4800,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 abs-send-time extension */
if(packet->video && handle->stream->abs_send_time_ext_id > 0) {
*index = (handle->stream->abs_send_time_ext_id << 4) + 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

  • 3 (24 bits), no?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, the length in the protocol field is always actual data length -1 when you write the extension.

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case we're writing the first byte of the extension, so the ID of the extension and the length field:

The 4-bit length is the number minus one of data bytes of this header extension element following the one-byte header.

/* We'll actually set the value later, when sending the packet */
memset(index+1, 0, 3);
index += 4;
extlen += 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;
Expand Down
4 changes: 2 additions & 2 deletions ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,8 @@ struct janus_ice_stream {
gint audiolevel_ext_id;
/*! \brief Video orientation extension ID */
gint videoorientation_ext_id;
/*! \brief Frame marking extension ID */
gint framemarking_ext_id;
/*! \brief Absolute Send Time ext ID */
gint abs_send_time_ext_id;
/*! \brief Whether we do transport wide cc for video */
gboolean do_transport_wide_cc;
/*! \brief Transport wide cc rtp ext ID */
Expand Down
58 changes: 54 additions & 4 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,34 @@ int janus_process_incoming_request(janus_request *request) {
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
}
janus_request_ice_handle_answer(handle, audio, video, data, jsep_sdp);
/* Check if the answer does contain the mid/abs-send-time/twcc extmaps */
gboolean do_mid = FALSE, do_twcc = FALSE, do_abs_send_time = FALSE;
GList *temp = parsed_sdp->m_lines;
while(temp) {
janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
GList *tempA = m->attributes;
while(tempA) {
janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
do_mid = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
do_twcc = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
do_abs_send_time = TRUE;
}
tempA = tempA->next;
}
temp = temp->next;
}
if(!do_mid && handle->stream)
handle->stream->mid_ext_id = 0;
if(!do_twcc && handle->stream) {
handle->stream->do_transport_wide_cc = FALSE;
handle->stream->transport_wide_cc_ext_id = 0;
}
if(!do_abs_send_time && handle->stream)
handle->stream->abs_send_time_ext_id = 0;
} else {
/* Check if the mid RTP extension is being negotiated */
handle->stream->mid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_MID);
Expand All @@ -1500,6 +1528,8 @@ int janus_process_incoming_request(janus_request *request) {
handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
/* Check if the video orientation ID extension is being negotiated */
handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
/* Check if the abs-send-time ID extension is being negotiated */
handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
/* Check if transport wide CC is supported */
int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
Expand Down Expand Up @@ -1561,6 +1591,8 @@ int janus_process_incoming_request(janus_request *request) {
handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
/* Check if the video orientation ID extension is being negotiated */
handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
/* Check if the abs-send-time ID extension is being negotiated */
handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
/* Check if transport wide CC is supported */
int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
Expand Down Expand Up @@ -3049,6 +3081,8 @@ json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
json_object_set_new(se, JANUS_RTP_EXTMAP_RID, json_integer(stream->rid_ext_id));
if(stream->ridrtx_ext_id > 0)
json_object_set_new(se, JANUS_RTP_EXTMAP_REPAIRED_RID, json_integer(stream->ridrtx_ext_id));
if(stream->abs_send_time_ext_id > 0)
json_object_set_new(se, JANUS_RTP_EXTMAP_ABS_SEND_TIME, json_integer(stream->abs_send_time_ext_id));
if(stream->transport_wide_cc_ext_id > 0)
json_object_set_new(se, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC, json_integer(stream->transport_wide_cc_ext_id));
if(stream->audiolevel_ext_id > 0)
Expand Down Expand Up @@ -3650,7 +3684,8 @@ 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, transport_wide_cc_ext_id = 0, audiolevel_ext_id = 0, videoorientation_ext_id = 0;
int mid_ext_id = 0, transport_wide_cc_ext_id = 0, abs_send_time_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 @@ -3662,6 +3697,8 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
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_ABS_SEND_TIME))
abs_send_time_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 @@ -3684,26 +3721,33 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
ice_handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
ice_handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
}
if(ice_handle->stream && ice_handle->stream->abs_send_time_ext_id != abs_send_time_ext_id)
ice_handle->stream->abs_send_time_ext_id = abs_send_time_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)
ice_handle->stream->videoorientation_ext_id = videoorientation_ext_id;
} else {
/* Check if the answer does contain the mid/rid/repaired-rid attributes */
gboolean do_mid = FALSE, do_rid = FALSE, do_repaired_rid = FALSE;
/* Check if the answer does contain the mid/rid/repaired-rid/abs-send-time/twcc extmaps */
gboolean do_mid = FALSE, do_rid = FALSE, do_repaired_rid = FALSE,
do_twcc = FALSE, do_abs_send_time = FALSE;
GList *temp = parsed_sdp->m_lines;
while(temp) {
janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
GList *tempA = m->attributes;
while(tempA) {
janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
if(a->name && a->value) {
if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
do_mid = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_RID))
do_rid = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID))
do_repaired_rid = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
do_twcc = TRUE;
else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
do_abs_send_time = TRUE;
}
tempA = tempA->next;
}
Expand All @@ -3727,6 +3771,12 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
}
if(!do_repaired_rid && ice_handle->stream)
ice_handle->stream->ridrtx_ext_id = 0;
if(!do_twcc && ice_handle->stream) {
ice_handle->stream->do_transport_wide_cc = FALSE;
ice_handle->stream->transport_wide_cc_ext_id = 0;
}
if(!do_abs_send_time && ice_handle->stream)
ice_handle->stream->abs_send_time_ext_id = 0;
}
if(!updating && !janus_ice_is_full_trickle_enabled()) {
/* Wait for candidates-done callback */
Expand Down
2 changes: 2 additions & 0 deletions plugins/janus_streaming.c
Original file line number Diff line number Diff line change
Expand Up @@ -4957,6 +4957,8 @@ static void *janus_streaming_handler(void *data) {
g_strlcat(sdptemp, "a=sendonly\r\n", 2048);
g_snprintf(buffer, 512, "a=extmap:%d %s\r\n", 1, JANUS_RTP_EXTMAP_MID);
g_strlcat(sdptemp, buffer, 2048);
g_snprintf(buffer, 512, "a=extmap:%d %s\r\n", 2, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
g_strlcat(sdptemp, buffer, 2048);
}
#ifdef HAVE_SCTP
if(mp->data && session->data) {
Expand Down
17 changes: 8 additions & 9 deletions plugins/janus_videoroom.c
Original file line number Diff line number Diff line change
Expand Up @@ -7695,14 +7695,14 @@ static void *janus_videoroom_handler(void *data) {
break;
mid_ext_id++;
}
int twcc_ext_id = 1;
while(twcc_ext_id < 15) {
if(twcc_ext_id != mid_ext_id &&
twcc_ext_id != participant->audio_level_extmap_id &&
twcc_ext_id != participant->video_orient_extmap_id &&
twcc_ext_id != participant->playout_delay_extmap_id)
int abs_send_time_ext_id = 1;
while(abs_send_time_ext_id < 15) {
if(abs_send_time_ext_id != mid_ext_id &&
abs_send_time_ext_id != participant->audio_level_extmap_id &&
abs_send_time_ext_id != participant->video_orient_extmap_id &&
abs_send_time_ext_id != participant->playout_delay_extmap_id)
break;
twcc_ext_id++;
abs_send_time_ext_id++;
}
offer = janus_sdp_generate_offer(s_name, answer->c_addr,
JANUS_SDP_OA_AUDIO, participant->audio,
Expand All @@ -7723,8 +7723,7 @@ static void *janus_videoroom_handler(void *data) {
participant->video_orient_extmap_id > 0 ? participant->video_orient_extmap_id : 0,
JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
participant->playout_delay_extmap_id > 0 ? participant->playout_delay_extmap_id : 0,
JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC,
videoroom->transport_wide_cc_ext ? twcc_ext_id : 0,
JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_ABS_SEND_TIME, abs_send_time_ext_id,
JANUS_SDP_OA_DATA, participant->data,
JANUS_SDP_OA_DONE);
/* Is this room recorded, or are we recording this publisher already? */
Expand Down
31 changes: 31 additions & 0 deletions rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,37 @@ int janus_rtp_header_extension_parse_rid(char *buf, int len, int id,
return 0;
}

int janus_rtp_header_extension_parse_abs_sent_time(char *buf, int len, int id, uint32_t *abs_ts) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
/* a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time */
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if(val_len < 3 || val_len > len-(ext-buf)-1)
return -3;
uint32_t abs24 = 0;
memcpy(&abs24, ext+1, 3);
if(abs_ts)
*abs_ts = ntohl(abs24 << 8);
return 0;
}

int janus_rtp_header_extension_set_abs_send_time(char *buf, int len, int id, uint32_t abs_ts) {
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 < 3 || val_len > len-(ext-buf)-1)
return -3;
uint32_t abs24 = htonl(abs_ts) >> 8;
memcpy(ext+1, &abs24, 3);
return 0;
}

int janus_rtp_header_extension_parse_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)
Expand Down
16 changes: 16 additions & 0 deletions rtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,22 @@ int janus_rtp_header_extension_parse_mid(char *buf, int len, int id,
int janus_rtp_header_extension_parse_rid(char *buf, int len, int id,
char *sdes_item, int sdes_len);

/*! \brief Helper to parse an abs-send-time RTP extension (http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time)
* @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] abs_ts Variable where the parsed abs-send-time value will be stored
* @returns 0 if found, -1 otherwise */
int janus_rtp_header_extension_parse_abs_sent_time(char *buf, int len, int id, uint32_t *abs_ts);

/*! \brief Helper to set an abs-send-time RTP extension (http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time)
* @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] abs_ts Absolute Send Time value to set
* @returns 0 if found, -1 otherwise */
int janus_rtp_header_extension_set_abs_send_time(char *buf, int len, int id, uint32_t abs_ts);

/*! \brief Helper to parse 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
Expand Down