Skip to content

Commit

Permalink
Merge pull request meetecho#1547 from meetecho/simulcast-rid
Browse files Browse the repository at this point in the history
Better support for rid-based simulcasting
  • Loading branch information
lminiero authored Mar 18, 2019
2 parents d665ff5 + 96b6478 commit 58be79e
Show file tree
Hide file tree
Showing 15 changed files with 489 additions and 217 deletions.
6 changes: 3 additions & 3 deletions html/janus.js
Original file line number Diff line number Diff line change
Expand Up @@ -2433,9 +2433,9 @@ function Janus(gatewayCallbacks) {
if(!parameters)
parameters = {};
parameters.encodings = [
{ rid: "high", active: true, priority: "high", maxBitrate: 1000000 },
{ rid: "medium", active: true, priority: "medium", maxBitrate: 300000 },
{ rid: "low", active: true, priority: "low", maxBitrate: 100000 }
{ rid: "l", active: true, maxBitrate: 100000 },
{ rid: "m", active: true, maxBitrate: 300000 },
{ rid: "h", active: true, maxBitrate: 1000000 }
];
sender.setParameters(parameters);
}
Expand Down
119 changes: 70 additions & 49 deletions ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -2160,59 +2160,76 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
|| stream->video_ssrc_peer[2] == packet_ssrc
|| stream->video_ssrc_peer_rtx[2] == packet_ssrc) ? 1 : 0);
if(!video && stream->audio_ssrc_peer != packet_ssrc) {
/* FIXME In case it happens, we should check what it is */
if(stream->audio_ssrc_peer == 0 || stream->video_ssrc_peer[0] == 0) {
/* Apparently we were not told the peer SSRCs, try the RTP mid extension (or payload types) */
gboolean found = FALSE;
guint16 pt = header->type;
if(handle->stream->mid_ext_id > 0) {
char sdes_item[16];
if(janus_rtp_header_extension_parse_mid(buf, len, handle->stream->mid_ext_id, sdes_item, sizeof(sdes_item)) == 0) {
if(handle->audio_mid && !strcmp(handle->audio_mid, sdes_item)) {
/* It's audio */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is audio! (mid %s)\n", handle->handle_id, packet_ssrc, sdes_item);
video = 0;
stream->audio_ssrc_peer = packet_ssrc;
found = TRUE;
} else if(handle->video_mid && !strcmp(handle->video_mid, sdes_item)) {
/* It's video */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is video! (mid %s)\n", handle->handle_id, packet_ssrc, sdes_item);
video = 1;
stream->video_ssrc_peer[0] = packet_ssrc;
found = TRUE;
}
}
}
if(!found && stream->audio_ssrc_peer == 0 && stream->audio_payload_types) {
GList *pts = stream->audio_payload_types;
while(pts) {
guint16 audio_pt = GPOINTER_TO_UINT(pts->data);
if(pt == audio_pt) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is audio! (payload type %"SCNu16")\n", handle->handle_id, packet_ssrc, pt);
video = 0;
stream->audio_ssrc_peer = packet_ssrc;
found = TRUE;
break;
}
pts = pts->next;
}
}
if(!found && stream->video_ssrc_peer[0] == 0 && stream->video_payload_types) {
GList *pts = stream->video_payload_types;
while(pts) {
guint16 video_pt = GPOINTER_TO_UINT(pts->data);
if(pt == video_pt) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is video! (payload type %"SCNu16")\n", handle->handle_id, packet_ssrc, pt);
video = 1;
/* Apparently we were not told the peer SSRCs, try the RTP mid extension (or payload types) */
gboolean found = FALSE;
if(handle->stream->mid_ext_id > 0) {
char sdes_item[16];
if(janus_rtp_header_extension_parse_mid(buf, len, handle->stream->mid_ext_id, sdes_item, sizeof(sdes_item)) == 0) {
if(handle->audio_mid && !strcmp(handle->audio_mid, sdes_item)) {
/* It's audio */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is audio! (mid %s)\n", handle->handle_id, packet_ssrc, sdes_item);
video = 0;
stream->audio_ssrc_peer = packet_ssrc;
found = TRUE;
} else if(handle->video_mid && !strcmp(handle->video_mid, sdes_item)) {
/* It's video */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Unadvertized SSRC (%"SCNu32") is video! (mid %s)\n", handle->handle_id, packet_ssrc, sdes_item);
video = 1;
/* Check if simulcasting is involved */
if(stream->rid[0] == NULL || stream->rid_ext_id < 1) {
stream->video_ssrc_peer[0] = packet_ssrc;
found = TRUE;
break;
} else {
if(janus_rtp_header_extension_parse_rid(buf, len, stream->rid_ext_id, sdes_item, sizeof(sdes_item)) == 0) {
/* Try the RTP stream ID */
if(stream->rid[0] != NULL && !strcmp(stream->rid[0], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting: rid=%s\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer[0] = packet_ssrc;
vindex = 0;
found = TRUE;
} else if(stream->rid[1] != NULL && !strcmp(stream->rid[1], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting #1: rid=%s\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer[1] = packet_ssrc;
vindex = 1;
found = TRUE;
} else if(stream->rid[2] != NULL && !strcmp(stream->rid[2], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting #2: rid=%s\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer[2] = packet_ssrc;
vindex = 2;
found = TRUE;
} else {
JANUS_LOG(LOG_WARN, "[%"SCNu64"] -- Simulcasting: unknown rid %s..?\n", handle->handle_id, sdes_item);
}
} else if(stream->ridrtx_ext_id > 0 &&
janus_rtp_header_extension_parse_rid(buf, len, stream->ridrtx_ext_id, sdes_item, sizeof(sdes_item)) == 0) {
/* Try the repaired RTP stream ID */
if(stream->rid[0] != NULL && !strcmp(stream->rid[0], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting: rid=%s (rtx)\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer_rtx[0] = packet_ssrc;
vindex = 0;
rtx = 1;
found = TRUE;
} else if(stream->rid[1] != NULL && !strcmp(stream->rid[1], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting #1: rid=%s (rtx)\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer_rtx[1] = packet_ssrc;
vindex = 1;
rtx = 1;
found = TRUE;
} else if(stream->rid[2] != NULL && !strcmp(stream->rid[2], sdes_item)) {
JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Simulcasting #2: rid=%s (rtx)\n", handle->handle_id, sdes_item);
stream->video_ssrc_peer_rtx[2] = packet_ssrc;
vindex = 2;
rtx = 1;
found = TRUE;
} else {
JANUS_LOG(LOG_WARN, "[%"SCNu64"] -- Simulcasting: unknown rid %s..?\n", handle->handle_id, sdes_item);
}
}
}
pts = pts->next;
}
}
}
if(!video && stream->audio_ssrc_peer != packet_ssrc) {
if(!found) {
JANUS_LOG(LOG_WARN, "[%"SCNu64"] Not video and not audio? dropping (SSRC %"SCNu32")...\n", handle->handle_id, packet_ssrc);
return;
}
Expand Down Expand Up @@ -2289,7 +2306,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
header->type = stream->video_payload_type;
packet_ssrc = stream->video_ssrc_peer[vindex];
header->ssrc = htonl(packet_ssrc);
if (plen > 0) {
if(plen > 0) {
memcpy(&header->seq_number, payload, 2);
/* Finally, remove the original sequence number from the payload: rather than moving
* the whole payload back two bytes, we shift the header forward (less bytes to move) */
Expand All @@ -2300,6 +2317,10 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
buf += 2;
payload +=2;
header = (janus_rtp_header *)buf;
if(stream->rid_ext_id > 1 && stream->ridrtx_ext_id > 1) {
/* Replace the 'repaired' extension ID as well with the 'regular' one */
janus_rtp_header_extension_replace_id(buf, buflen, stream->ridrtx_ext_id, stream->rid_ext_id);
}
}
}
/* Check if we need to handle transport wide cc */
Expand Down Expand Up @@ -2471,7 +2492,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
/* If this is video, check if this is a keyframe: if so, we empty our NACK queue */
if(video && stream->video_is_keyframe) {
if(stream->video_is_keyframe(payload, plen)) {
if(component->last_seqs_video[vindex] && (int16_t)(new_seqn - rtcp_ctx->max_seq_nr) > 0) {
if(rtcp_ctx && (int16_t)(new_seqn - rtcp_ctx->max_seq_nr) > 0) {
JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Keyframe received with a highest sequence number, resetting NACK queue\n", handle->handle_id);
janus_seq_list_free(&component->last_seqs_video[vindex]);
}
Expand Down
4 changes: 4 additions & 0 deletions ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ struct janus_ice_stream {
guint32 video_ssrc_peer_rtx[3], video_ssrc_peer_rtx_new[3], video_ssrc_peer_rtx_orig[3];
/*! \brief Array of RTP Stream IDs (for Firefox simulcasting, if enabled) */
char *rid[3];
/*! \brief Whether we should use the legacy simulcast syntax (a=simulcast:recv rid=..) or the proper one (a=simulcast:recv ..) */
gboolean legacy_rid;
/*! \brief RTP switching context(s) in case of renegotiations (audio+video and/or simulcast) */
janus_rtp_switching_context rtp_ctx[3];
/*! \brief List of payload types we can expect for audio */
Expand Down Expand Up @@ -388,6 +390,8 @@ struct janus_ice_stream {
guint32 video_last_ts;
/*! \brief SDES mid RTP extension ID */
gint mid_ext_id;
/*! \brief RTP Stream extension ID, and the related rtx one */
gint rid_ext_id, ridrtx_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
80 changes: 70 additions & 10 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,9 @@ int janus_process_incoming_request(janus_request *request) {
} 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);
/* Check if the RTP Stream ID extension is being negotiated */
handle->stream->rid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_RID);
handle->stream->ridrtx_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_REPAIRED_RID);
/* 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 @@ -1328,14 +1331,29 @@ int janus_process_incoming_request(janus_request *request) {
json_t *body_jsep = NULL;
if(jsep_sdp_stripped) {
body_jsep = json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped);
/* Check if VP8 simulcasting is enabled */
/* Check if simulcasting is enabled */
if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
if(handle->stream && handle->stream->video_ssrc_peer[1]) {
if(handle->stream && (handle->stream->rid[0] || handle->stream->video_ssrc_peer[1])) {
json_t *simulcast = json_object();
json_object_set_new(simulcast, "ssrc-0", json_integer(handle->stream->video_ssrc_peer[0]));
json_object_set_new(simulcast, "ssrc-1", json_integer(handle->stream->video_ssrc_peer[1]));
if(handle->stream->video_ssrc_peer[2])
json_object_set_new(simulcast, "ssrc-2", json_integer(handle->stream->video_ssrc_peer[2]));
/* If we have rids, pass those, otherwise pass the SSRCs */
if(handle->stream->rid[0]) {
json_t *rids = json_array();
json_array_append_new(rids, json_string(handle->stream->rid[0]));
if(handle->stream->rid[1])
json_array_append_new(rids, json_string(handle->stream->rid[1]));
if(handle->stream->rid[2])
json_array_append_new(rids, json_string(handle->stream->rid[2]));
json_object_set_new(simulcast, "rids", rids);
json_object_set_new(simulcast, "rid-ext", json_integer(handle->stream->rid_ext_id));
} else {
json_t *ssrcs = json_array();
json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[0]));
if(handle->stream->video_ssrc_peer[1])
json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[1]));
if(handle->stream->video_ssrc_peer[2])
json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[2]));
json_object_set_new(simulcast, "ssrcs", ssrcs);
}
json_object_set_new(body_jsep, "simulcast", simulcast);
}
}
Expand Down Expand Up @@ -2350,16 +2368,22 @@ json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
json_object_set_new(ss, "video-peer-sim-1-rtx", json_integer(stream->video_ssrc_peer_rtx[1]));
if(stream->video_ssrc_peer_rtx[2])
json_object_set_new(ss, "video-peer-sim-2-rtx", json_integer(stream->video_ssrc_peer_rtx[2]));
json_object_set_new(s, "ssrc", ss);
if(stream->rid[0]) {
json_t *sr = json_object();
json_t *rid = json_array();
json_array_append_new(rid, json_string(stream->rid[0]));
if(stream->rid[1])
json_array_append_new(rid, json_string(stream->rid[1]));
if(stream->rid[1])
json_array_append_new(rid, json_string(stream->rid[2]));
json_object_set_new(ss, "rid", rid);
json_object_set_new(sr, "rid", rid);
json_object_set_new(sr, "rid-ext-id", json_integer(stream->rid_ext_id));
json_object_set_new(sr, "ridrtx-ext-id", json_integer(stream->ridrtx_ext_id));
if(stream->legacy_rid)
json_object_set_new(sr, "rid-syntax", json_string("legacy"));
json_object_set_new(s, "rid-simulcast", sr);
}
json_object_set_new(s, "ssrc", ss);
json_t *sd = json_object();
json_object_set_new(sd, "audio-send", stream->audio_send ? json_true() : json_false());
json_object_set_new(sd, "audio-recv", stream->audio_recv ? json_true() : json_false());
Expand Down Expand Up @@ -2941,15 +2965,16 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
}
}
}
/* Make sure we don't send the mid attribute when offering ourselves */
/* Make sure we don't send the mid/rid/repaired-rid attributes when offering ourselves */
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 && (strstr(a->value, JANUS_RTP_EXTMAP_MID) ||
strstr(a->value, JANUS_RTP_EXTMAP_RTP_STREAM_ID))) {
strstr(a->value, JANUS_RTP_EXTMAP_RID) ||
strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID))) {
m->attributes = g_list_remove(m->attributes, a);
tempA = m->attributes;
janus_sdp_attribute_destroy(a);
Expand All @@ -2959,6 +2984,41 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
}
temp = temp->next;
}
} else {
/* Check if the answer does contain the mid/rid/repaired-rid attributes */
gboolean do_mid = FALSE, do_rid = FALSE, do_repaired_rid = 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(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;
}
tempA = tempA->next;
}
temp = temp->next;
}
if(!do_mid)
ice_handle->stream->mid_ext_id = 0;
if(!do_rid) {
ice_handle->stream->rid_ext_id = 0;
ice_handle->stream->ridrtx_ext_id = 0;
g_free(ice_handle->stream->rid[0]);
ice_handle->stream->rid[0] = NULL;
g_free(ice_handle->stream->rid[1]);
ice_handle->stream->rid[1] = NULL;
g_free(ice_handle->stream->rid[2]);
ice_handle->stream->rid[2] = NULL;
}
if(!do_repaired_rid)
ice_handle->stream->ridrtx_ext_id = 0;
}
if(!updating && !janus_ice_is_full_trickle_enabled()) {
/* Wait for candidates-done callback */
Expand Down
Loading

0 comments on commit 58be79e

Please sign in to comment.