[Pkg-voip-commits] [janus] 263/282: Handle RTCP for all remote SSRCs, including video simulcast

Jonas Smedegaard dr at jones.dk
Wed Dec 20 21:53:47 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to annotated tag debian/0.2.6-1
in repository janus.

commit 05449073c9c4955425e7c144af13e1bba8ce8db5
Author: Lorenzo Miniero <lminiero at gmail.com>
Date:   Fri Dec 15 19:17:53 2017 +0100

    Handle RTCP for all remote SSRCs, including video simulcast
---
 dtls.c  |   4 +-
 ice.c   | 337 ++++++++++++++++++++++++++++++++--------------------------------
 ice.h   |  69 ++++++-------
 janus.c | 167 ++++++++++++++++++--------------
 rtcp.c  |  12 +--
 sdp.c   |  36 ++++---
 6 files changed, 325 insertions(+), 300 deletions(-)

diff --git a/dtls.c b/dtls.c
index 25a5aa1..8204066 100644
--- a/dtls.c
+++ b/dtls.c
@@ -872,8 +872,8 @@ void janus_dtls_fd_bridge(janus_dtls_srtp *dtls) {
 		/* Update stats (TODO Do the same for the last second window as well)
 		 * FIXME: the Data stats includes the bytes used for the handshake */
 		if(bytes > 0) {
-			component->out_stats.data_packets++;
-			component->out_stats.data_bytes += bytes;
+			component->out_stats.data.packets++;
+			component->out_stats.data.bytes += bytes;
 		}
 		/* Check if there's anything left to send (e.g., fragmented packets) */
 		pending = BIO_ctrl_pending(dtls->filter_bio);
diff --git a/ice.c b/ice.c
index 42c69cd..6edba7b 100644
--- a/ice.c
+++ b/ice.c
@@ -988,25 +988,17 @@ static void janus_ice_stats_queue_free(gpointer data) {
 void janus_ice_stats_reset(janus_ice_stats *stats) {
 	if(stats == NULL)
 		return;
-	stats->audio_packets = 0;
-	stats->audio_bytes = 0;
-	if(stats->audio_bytes_lastsec)
-		g_queue_free_full(stats->audio_bytes_lastsec, &janus_ice_stats_queue_free);
-	stats->audio_bytes_lastsec = NULL;
-	stats->audio_notified_lastsec = FALSE;
-	stats->audio_nacks = 0;
-	stats->video_packets = 0;
-	stats->video_bytes = 0;
-	if(stats->video_bytes_lastsec)
-		g_queue_free_full(stats->video_bytes_lastsec, &janus_ice_stats_queue_free);
-	stats->video_bytes_lastsec = NULL;
-	stats->video_notified_lastsec = FALSE;
-	stats->video_nacks = 0;
-	stats->data_packets = 0;
-	stats->data_bytes = 0;
-	stats->last_slowlink_time = 0;
-	stats->sl_nack_period_ts = 0;
-	stats->sl_nack_recent_cnt = 0;
+	if(stats->audio.bytes_lastsec)
+		g_queue_free_full(stats->audio.bytes_lastsec, &janus_ice_stats_queue_free);
+	if(stats->video[0].bytes_lastsec)
+		g_queue_free_full(stats->video[0].bytes_lastsec, &janus_ice_stats_queue_free);
+	if(stats->video[1].bytes_lastsec)
+		g_queue_free_full(stats->video[1].bytes_lastsec, &janus_ice_stats_queue_free);
+	if(stats->video[2].bytes_lastsec)
+		g_queue_free_full(stats->video[2].bytes_lastsec, &janus_ice_stats_queue_free);
+	if(stats->data.bytes_lastsec)
+		g_queue_free_full(stats->data.bytes_lastsec, &janus_ice_stats_queue_free);
+	memset(stats, 0, sizeof(*stats));
 }
 
 
@@ -1390,12 +1382,20 @@ void janus_ice_stream_free(GHashTable *streams, janus_ice_stream *stream) {
 	stream->video_payload_types = NULL;
 	g_free(stream->audio_rtcp_ctx);
 	stream->audio_rtcp_ctx = NULL;
-	g_free(stream->video_rtcp_ctx);
-	stream->video_rtcp_ctx = NULL;
+	g_free(stream->video_rtcp_ctx[0]);
+	stream->video_rtcp_ctx[0] = NULL;
+	g_free(stream->video_rtcp_ctx[1]);
+	stream->video_rtcp_ctx[1] = NULL;
+	g_free(stream->video_rtcp_ctx[2]);
+	stream->video_rtcp_ctx[2] = NULL;
 	stream->audio_first_ntp_ts = 0;
 	stream->audio_first_rtp_ts = 0;
-	stream->video_first_ntp_ts = 0;
-	stream->video_first_rtp_ts = 0;
+	stream->video_first_ntp_ts[0] = 0;
+	stream->video_first_ntp_ts[1] = 0;
+	stream->video_first_ntp_ts[2] = 0;
+	stream->video_first_rtp_ts[0] = 0;
+	stream->video_first_rtp_ts[1] = 0;
+	stream->video_first_rtp_ts[2] = 0;
 	stream->audio_last_ts = 0;
 	stream->video_last_ts = 0;
 	g_free(stream);
@@ -1482,8 +1482,12 @@ void janus_ice_component_free(GHashTable *components, janus_ice_component *compo
 	component->selected_pair = NULL;
 	if(component->last_seqs_audio)
 		janus_seq_list_free(&component->last_seqs_audio);
-	if(component->last_seqs_video)
-		janus_seq_list_free(&component->last_seqs_video);
+	if(component->last_seqs_video[0])
+		janus_seq_list_free(&component->last_seqs_video[0]);
+	if(component->last_seqs_video[1])
+		janus_seq_list_free(&component->last_seqs_video[1]);
+	if(component->last_seqs_video[2])
+		janus_seq_list_free(&component->last_seqs_video[2]);
 	janus_ice_stats_reset(&component->in_stats);
 	janus_ice_stats_reset(&component->out_stats);
 	g_free(component);
@@ -2006,8 +2010,8 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 		JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Looks like DTLS!\n", handle->handle_id);
 		janus_dtls_srtp_incoming_msg(component->dtls, buf, len);
 		/* Update stats (TODO Do the same for the last second window as well) */
-		component->in_stats.data_packets++;
-		component->in_stats.data_bytes += len;
+		component->in_stats.data.packets++;
+		component->in_stats.data.bytes += len;
 		return;
 	}
 	/* Not DTLS... RTP or RTCP? (http://tools.ietf.org/html/rfc5761#section-4) */
@@ -2023,19 +2027,19 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 			janus_rtp_header *header = (janus_rtp_header *)buf;
 			guint32 packet_ssrc = ntohl(header->ssrc);
 			/* Is this audio or video? */
-			int video = 0;
+			int video = 0, vindex = 0;
 			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
 				/* Easy enough */
 				video = (stream->stream_id == handle->video_id ? 1 : 0);
 			} else {
 				/* Bundled streams, check SSRC */
-				video = ((stream->video_ssrc_peer == packet_ssrc
+				video = ((stream->video_ssrc_peer[0] == packet_ssrc
 					|| stream->video_ssrc_peer_rtx == packet_ssrc
-					|| stream->video_ssrc_peer_sim_1 == packet_ssrc
-					|| stream->video_ssrc_peer_sim_2 == packet_ssrc) ? 1 : 0);
+					|| stream->video_ssrc_peer[1] == packet_ssrc
+					|| stream->video_ssrc_peer[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) {
+					if(stream->audio_ssrc_peer == 0 || stream->video_ssrc_peer[0] == 0) {
 						/* Apparently we were not told the peer SSRCs, try to guess from the payload type */
 						gboolean found = FALSE;
 						guint16 pt = header->type;
@@ -2053,14 +2057,14 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 								pts = pts->next;
 							}
 						}
-						if(!found && stream->video_ssrc_peer == 0 && stream->video_payload_types) {
+						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;
-									stream->video_ssrc_peer = packet_ssrc;
+									stream->video_ssrc_peer[0] = packet_ssrc;
 									found = TRUE;
 									break;
 								}
@@ -2073,19 +2077,22 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 						return;
 					}
 				}
+			}
+			if(video) {
 				if(stream->video_ssrc_peer_rtx == packet_ssrc) {
-					/* FIXME This is a video retransmission: set the regular peer SSRC so
-					 * that we avoid outgoing SRTP errors in case we got the packet already */
-					header->ssrc = htonl(stream->video_ssrc_peer);
-				} else if(stream->video_ssrc_peer_sim_1 == packet_ssrc) {
+					/* FIXME This is a video retransmission using RFC4588, but we don't support it yet,
+					 * see https://tools.ietf.org/html/rfc4588#section-4 */
+					JANUS_LOG(LOG_WARN, "[%"SCNu64"] RFC4588 rtx packet, dropping (SSRC %"SCNu32")...\n", handle->handle_id, packet_ssrc);
+					return;
+				} else if(stream->video_ssrc_peer[1] == packet_ssrc) {
 					/* FIXME Simulcast (1) */
 					JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Simulcast #1 (SSRC %"SCNu32")...\n", handle->handle_id, packet_ssrc);
-				} else if(stream->video_ssrc_peer_sim_2 == packet_ssrc) {
+					vindex = 1;
+				} else if(stream->video_ssrc_peer[2] == packet_ssrc) {
 					/* FIXME Simulcast (2) */
 					JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Simulcast #2 (SSRC %"SCNu32")...\n", handle->handle_id, packet_ssrc);
+					vindex = 2;
 				}
-				//~ JANUS_LOG(LOG_VERB, "[RTP] Bundling: this is %s (video=%"SCNu64", audio=%"SCNu64", got %ld)\n",
-					//~ video ? "video" : "audio", stream->video_ssrc_peer, stream->audio_ssrc_peer, ntohl(header->ssrc));
 			}
 
 			int buflen = len;
@@ -2100,9 +2107,9 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 				}
 			} else {
 				if(video) {
-					if(stream->video_ssrc_peer == 0) {
-						stream->video_ssrc_peer = ntohl(header->ssrc);
-						JANUS_LOG(LOG_VERB, "[%"SCNu64"]     Peer video SSRC: %u\n", handle->handle_id, stream->video_ssrc_peer);
+					if(stream->video_ssrc_peer[0] == 0) {
+						stream->video_ssrc_peer[0] = ntohl(header->ssrc);
+						JANUS_LOG(LOG_VERB, "[%"SCNu64"]     Peer video SSRC: %u\n", handle->handle_id, stream->video_ssrc_peer[0]);
 					}
 				} else {
 					if(stream->audio_ssrc_peer == 0) {
@@ -2126,45 +2133,41 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 					s->when = janus_get_monotonic_time();
 					janus_mutex_lock(&component->mutex);
 					if(!video) {
-						if(component->in_stats.audio_bytes == 0 || component->in_stats.audio_notified_lastsec) {
+						if(component->in_stats.audio.bytes == 0 || component->in_stats.audio.notified_lastsec) {
 							/* We either received our first audio packet, or we started receiving it again after missing more than a second */
-							component->in_stats.audio_notified_lastsec = FALSE;
+							component->in_stats.audio.notified_lastsec = FALSE;
 							janus_ice_notify_media(handle, FALSE, TRUE);
 						}
-						component->in_stats.audio_packets++;
-						component->in_stats.audio_bytes += buflen;
-						if(component->in_stats.audio_bytes_lastsec == NULL)
-							component->in_stats.audio_bytes_lastsec = g_queue_new();
-						g_queue_push_tail(component->in_stats.audio_bytes_lastsec, s);
-						if(g_queue_get_length(component->in_stats.audio_bytes_lastsec) > 100) {
-							s = (janus_ice_stats_item *)g_queue_pop_head(component->in_stats.audio_bytes_lastsec);
+						component->in_stats.audio.packets++;
+						component->in_stats.audio.bytes += buflen;
+						if(component->in_stats.audio.bytes_lastsec == NULL)
+							component->in_stats.audio.bytes_lastsec = g_queue_new();
+						g_queue_push_tail(component->in_stats.audio.bytes_lastsec, s);
+						if(g_queue_get_length(component->in_stats.audio.bytes_lastsec) > 100) {
+							s = (janus_ice_stats_item *)g_queue_pop_head(component->in_stats.audio.bytes_lastsec);
 							g_free(s);
 						}
 					} else {
-						if(component->in_stats.video_bytes == 0 || component->in_stats.video_notified_lastsec) {
+						if(component->in_stats.video[vindex].bytes == 0 || component->in_stats.video[vindex].notified_lastsec) {
 							/* We either received our first video packet, or we started receiving it again after missing more than a second */
-							component->in_stats.video_notified_lastsec = FALSE;
+							component->in_stats.video[vindex].notified_lastsec = FALSE;
 							janus_ice_notify_media(handle, TRUE, TRUE);
 						}
-						component->in_stats.video_packets++;
-						component->in_stats.video_bytes += buflen;
-						if(component->in_stats.video_bytes_lastsec == NULL)
-							component->in_stats.video_bytes_lastsec = g_queue_new();
-						g_queue_push_tail(component->in_stats.video_bytes_lastsec, s);
-						if(g_queue_get_length(component->in_stats.video_bytes_lastsec) > 100) {
-							s = (janus_ice_stats_item *)g_queue_pop_head(component->in_stats.video_bytes_lastsec);
+						component->in_stats.video[vindex].packets++;
+						component->in_stats.video[vindex].bytes += buflen;
+						if(component->in_stats.video[vindex].bytes_lastsec == NULL)
+							component->in_stats.video[vindex].bytes_lastsec = g_queue_new();
+						g_queue_push_tail(component->in_stats.video[vindex].bytes_lastsec, s);
+						if(g_queue_get_length(component->in_stats.video[vindex].bytes_lastsec) > 100) {
+							s = (janus_ice_stats_item *)g_queue_pop_head(component->in_stats.video[vindex].bytes_lastsec);
 							g_free(s);
 						}
 					}
 					janus_mutex_unlock(&component->mutex);
 				}
 
-				/* FIXME Don't handle RTCP or stats for the simulcasted SSRCs, for now */
-				if(video && packet_ssrc != stream->video_ssrc_peer)
-					return;
-
 				/* Update the RTCP context as well */
-				rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx : stream->audio_rtcp_ctx;
+				rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx[vindex] : stream->audio_rtcp_ctx;
 				janus_rtcp_process_incoming_rtp(rtcp_ctx, buf, buflen);
 
 				/* Keep track of RTP sequence numbers, in case we need to NACK them */
@@ -2177,7 +2180,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 				guint16 cur_seqn;
 				int last_seqs_len = 0;
 				janus_mutex_lock(&component->mutex);
-				janus_seq_info **last_seqs = video ? &component->last_seqs_video : &component->last_seqs_audio;
+				janus_seq_info **last_seqs = video ? &component->last_seqs_video[vindex] : &component->last_seqs_audio;
 				janus_seq_info *cur_seq = *last_seqs;
 				if(cur_seq) {
 					cur_seq = cur_seq->prev;
@@ -2189,8 +2192,8 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 				if(!janus_seq_in_range(new_seqn, cur_seqn, LAST_SEQS_MAX_LEN) &&
 						!janus_seq_in_range(cur_seqn, new_seqn, 1000)) {
 					/* Jump too big, start fresh */
-					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Big sequence number jump %hu -> %hu (%s stream)\n",
-						handle->handle_id, cur_seqn, new_seqn, video ? "video" : "audio");
+					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Big sequence number jump %hu -> %hu (%s stream #%d)\n",
+						handle->handle_id, cur_seqn, new_seqn, video ? "video" : "audio", vindex);
 					janus_seq_list_free(last_seqs);
 					cur_seq = NULL;
 					cur_seqn = new_seqn - (guint16)1;
@@ -2251,9 +2254,9 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 					/* Update stats */
 					component->nack_sent_recent_cnt += nacks_count;
 					if(video) {
-						component->out_stats.video_nacks += nacks_count;
+						component->out_stats.video[vindex].nacks += nacks_count;
 					} else {
-						component->out_stats.audio_nacks += nacks_count;
+						component->out_stats.audio.nacks += nacks_count;
 					}
 					/* Inform the plugin about the slow downlink in case it's needed */
 					janus_slow_link_update(component, handle, nacks_count, video, 0, now);
@@ -2295,7 +2298,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got RTCP BYE on stream %"SCNu16" (component %"SCNu16")\n", handle->handle_id, stream->stream_id, component->component_id);
 				}
 				/* Is this audio or video? */
-				int video = 0;
+				int video = 0, vindex = 0;
 				if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
 					/* Easy enough */
 					video = (stream->stream_id == handle->video_id ? 1 : 0);
@@ -2310,7 +2313,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 						JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is audio (no video has been negotiated)\n", handle->handle_id);
 						video = 0;
 					} else {
-						if(stream->audio_ssrc_peer == 0 || stream->video_ssrc_peer == 0) {
+						if(stream->audio_ssrc_peer == 0 || stream->video_ssrc_peer[0] == 0) {
 							/* We don't know the remote SSRC: this can happen for recvonly clients
 							 * (see https://groups.google.com/forum/#!topic/discuss-webrtc/5yuZjV7lkNc)
 							 * Check the local SSRC, compare it to what we have */
@@ -2328,29 +2331,28 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 							JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is %s (local SSRC: video=%"SCNu32", audio=%"SCNu32", got %"SCNu32")\n",
 								handle->handle_id, video ? "video" : "audio", stream->video_ssrc, stream->audio_ssrc, rtcp_ssrc);
 						} else {
-							/* Check the remote SSRC, compare it to what we have */
+							/* Check the remote SSRC, compare it to what we have: in case
+							 * we're simulcasting, let's compare to the other SSRCs too */
 							guint32 rtcp_ssrc = janus_rtcp_get_sender_ssrc(buf, len);
 							if(rtcp_ssrc == stream->audio_ssrc_peer) {
 								video = 0;
-							} else if(rtcp_ssrc == stream->video_ssrc_peer) {
+							} else if(rtcp_ssrc == stream->video_ssrc_peer[0]) {
 								video = 1;
-							} else {
-								/* If we're simulcasting, let's compare to the other SSRCs too */
-								if((stream->video_ssrc_peer_sim_1 && rtcp_ssrc == stream->video_ssrc_peer_sim_1) ||
-										(stream->video_ssrc_peer_sim_2 && rtcp_ssrc == stream->video_ssrc_peer_sim_2)) {
-									/* FIXME RTCP for simulcasting SSRC, let's drop it for now... */
-									JANUS_LOG(LOG_HUGE, "Dropping RTCP packet for SSRC %"SCNu32"\n", rtcp_ssrc);
-									return;
-								}
+							} else if(stream->video_ssrc_peer[1] && rtcp_ssrc == stream->video_ssrc_peer[1]) {
+								video = 1;
+								vindex = 1;
+							} else if(stream->video_ssrc_peer[2] && rtcp_ssrc == stream->video_ssrc_peer[2]) {
+								video = 1;
+								vindex = 2;
 							}
-							JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is %s (remote SSRC: video=%"SCNu32", audio=%"SCNu32", got %"SCNu32")\n",
-								handle->handle_id, video ? "video" : "audio", stream->video_ssrc_peer, stream->audio_ssrc_peer, rtcp_ssrc);
+							JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is %s (remote SSRC: video=%"SCNu32" #%d, audio=%"SCNu32", got %"SCNu32")\n",
+								handle->handle_id, video ? "video" : "audio", stream->video_ssrc_peer[vindex], vindex, stream->audio_ssrc_peer, rtcp_ssrc);
 						}
 					}
 				}
 
 				/* Let's process this RTCP (compound?) packet, and update the RTCP context for this stream in case */
-				rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx : stream->audio_rtcp_ctx;
+				rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx[vindex] : stream->audio_rtcp_ctx;
 				janus_rtcp_parse(rtcp_ctx, buf, buflen);
 
 				/* Now let's see if there are any NACKs to handle */
@@ -2402,9 +2404,9 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 					buflen = janus_rtcp_remove_nacks(buf, buflen);
 					/* Update stats */
 					if(video) {
-						component->in_stats.video_nacks += nacks_count;
+						component->in_stats.video[vindex].nacks += nacks_count;
 					} else {
-						component->in_stats.audio_nacks += nacks_count;
+						component->in_stats.audio.nacks += nacks_count;
 					}
 					/* Inform the plugin about the slow uplink in case it's needed */
 					janus_slow_link_update(component, handle, retransmits_cnt, video, 1, now);
@@ -2433,8 +2435,8 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
 		janus_dtls_srtp_incoming_msg(component->dtls, buf, len);
 		/* Update stats (TODO Do the same for the last second window as well) */
 		if(len > 0) {
-			component->in_stats.data_packets++;
-			component->in_stats.data_bytes += len;
+			component->in_stats.data.packets++;
+			component->in_stats.data.bytes += len;
 		}
 		return;
 	}
@@ -2988,20 +2990,14 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi
 		/* FIXME By default, if we're being called we're DTLS clients, but this may be changed by ICE... */
 		audio_stream->dtls_role = offer ? JANUS_DTLS_ROLE_CLIENT : JANUS_DTLS_ROLE_ACTPASS;
 		audio_stream->audio_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
-		audio_stream->audio_ssrc_peer = 0;	/* FIXME Right now we don't know what this will be */
-		audio_stream->video_ssrc = 0;
 		if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
 			/* If we're bundling, this stream is going to be used for video as well */
 			audio_stream->video_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
 		}
-		audio_stream->video_ssrc_peer = 0;	/* FIXME Right now we don't know what this will be */
-		audio_stream->video_ssrc_peer_rtx = 0;		/* FIXME Right now we don't know if and what this will be */
-		audio_stream->video_ssrc_peer_sim_1 = 0;	/* FIXME Right now we don't know if and what this will be */
-		audio_stream->video_ssrc_peer_sim_2 = 0;	/* FIXME Right now we don't know if and what this will be */
 		audio_stream->audio_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
 		audio_stream->audio_rtcp_ctx->tb = 48000;	/* May change later */
-		audio_stream->video_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
-		audio_stream->video_rtcp_ctx->tb = 90000;
+		audio_stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
+		audio_stream->video_rtcp_ctx[0]->tb = 90000;
 		audio_stream->noerrorlog = FALSE;
 		janus_mutex_init(&audio_stream->mutex);
 		audio_stream->components = g_hash_table_new(NULL, NULL);
@@ -3061,7 +3057,7 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi
 		audio_rtp->nack_sent_log_ts = 0;
 		audio_rtp->nack_sent_recent_cnt = 0;
 		audio_rtp->last_seqs_audio = NULL;
-		audio_rtp->last_seqs_video = NULL;
+		audio_rtp->last_seqs_video[0] = NULL;
 		janus_ice_stats_reset(&audio_rtp->in_stats);
 		janus_ice_stats_reset(&audio_rtp->out_stats);
 		janus_mutex_init(&audio_rtp->mutex);
@@ -3157,14 +3153,8 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi
 		/* FIXME By default, if we're being called we're DTLS clients, but this may be changed by ICE... */
 		video_stream->dtls_role = offer ? JANUS_DTLS_ROLE_CLIENT : JANUS_DTLS_ROLE_ACTPASS;
 		video_stream->video_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
-		video_stream->video_ssrc_peer = 0;	/* FIXME Right now we don't know what this will be */
-		video_stream->video_ssrc_peer_rtx = 0;		/* FIXME Right now we don't know if and what this will be */
-		video_stream->video_ssrc_peer_sim_1 = 0;	/* FIXME Right now we don't know if and what this will be */
-		video_stream->video_ssrc_peer_sim_2 = 0;	/* FIXME Right now we don't know if and what this will be */
-		video_stream->audio_ssrc = 0;
-		video_stream->audio_ssrc_peer = 0;
-		video_stream->video_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
-		video_stream->video_rtcp_ctx->tb = 90000;
+		video_stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
+		video_stream->video_rtcp_ctx[0]->tb = 90000;
 		video_stream->components = g_hash_table_new(NULL, NULL);
 		video_stream->noerrorlog = FALSE;
 		janus_mutex_init(&video_stream->mutex);
@@ -3224,7 +3214,7 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi
 		video_rtp->nack_sent_log_ts = 0;
 		video_rtp->nack_sent_recent_cnt = 0;
 		video_rtp->last_seqs_audio = NULL;
-		video_rtp->last_seqs_video = NULL;
+		video_rtp->last_seqs_video[0] = NULL;
 		janus_ice_stats_reset(&video_rtp->in_stats);
 		janus_ice_stats_reset(&video_rtp->out_stats);
 		janus_mutex_init(&video_rtp->mutex);
@@ -3480,18 +3470,18 @@ void *janus_ice_send_thread(void *data) {
 		if(no_media_timer > 0 && now-before >= G_USEC_PER_SEC) {
 			if(handle->audio_stream && handle->audio_stream->rtp_component) {
 				janus_ice_component *component = handle->audio_stream->rtp_component;
-				janus_ice_stats_item *last = (janus_ice_stats_item *)(component->in_stats.audio_bytes_lastsec ? g_queue_peek_tail(component->in_stats.audio_bytes_lastsec) : NULL);
-				if(!component->in_stats.audio_notified_lastsec && last && now-last->when >= (gint64)no_media_timer*G_USEC_PER_SEC) {
+				janus_ice_stats_item *last = (janus_ice_stats_item *)(component->in_stats.audio.bytes_lastsec ? g_queue_peek_tail(component->in_stats.audio.bytes_lastsec) : NULL);
+				if(!component->in_stats.audio.notified_lastsec && last && now-last->when >= (gint64)no_media_timer*G_USEC_PER_SEC) {
 					/* We missed more than no_second_timer seconds of audio! */
-					component->in_stats.audio_notified_lastsec = TRUE;
+					component->in_stats.audio.notified_lastsec = TRUE;
 					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive audio for more than %d seconds...\n", handle->handle_id, no_media_timer);
 					janus_ice_notify_media(handle, FALSE, FALSE);
 				}
-				if(!component->in_stats.video_notified_lastsec && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
-					last = (janus_ice_stats_item *)(component->in_stats.video_bytes_lastsec ? g_queue_peek_tail(component->in_stats.video_bytes_lastsec) : NULL);
+				if(!component->in_stats.video[0].notified_lastsec && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
+					last = (janus_ice_stats_item *)(component->in_stats.video[0].bytes_lastsec ? g_queue_peek_tail(component->in_stats.video[0].bytes_lastsec) : NULL);
 					if(last && now-last->when >= (gint64)no_media_timer*G_USEC_PER_SEC) {
 						/* We missed more than no_second_timer seconds of video! */
-						component->in_stats.video_notified_lastsec = TRUE;
+						component->in_stats.video[0].notified_lastsec = TRUE;
 						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive video for more than %d seconds...\n", handle->handle_id, no_media_timer);
 						janus_ice_notify_media(handle, TRUE, FALSE);
 					}
@@ -3499,10 +3489,10 @@ void *janus_ice_send_thread(void *data) {
 			}
 			if(handle->video_stream && handle->video_stream->rtp_component) {
 				janus_ice_component *component = handle->video_stream->rtp_component;
-				janus_ice_stats_item *last = (janus_ice_stats_item *)(component->in_stats.video_bytes_lastsec ? g_queue_peek_tail(component->in_stats.video_bytes_lastsec) : NULL);
-				if(!component->in_stats.video_notified_lastsec && last && now-last->when >= (gint64)no_media_timer*G_USEC_PER_SEC) {
+				janus_ice_stats_item *last = (janus_ice_stats_item *)(component->in_stats.video[0].bytes_lastsec ? g_queue_peek_tail(component->in_stats.video[0].bytes_lastsec) : NULL);
+				if(!component->in_stats.video[0].notified_lastsec && last && now-last->when >= (gint64)no_media_timer*G_USEC_PER_SEC) {
 					/* We missed more than no_second_timer seconds of video! */
-					component->in_stats.video_notified_lastsec = TRUE;
+					component->in_stats.video[0].notified_lastsec = TRUE;
 					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive video for more than a second...\n", handle->handle_id);
 					janus_ice_notify_media(handle, TRUE, FALSE);
 				}
@@ -3531,7 +3521,7 @@ void *janus_ice_send_thread(void *data) {
 		if(now-video_rtcp_last_rr >= 5*G_USEC_PER_SEC) {
 			janus_ice_stream *stream = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? (handle->audio_stream ? handle->audio_stream : handle->video_stream) : (handle->video_stream);
 			if(stream) {
-				if(stream->video_rtcp_ctx && stream->video_rtcp_ctx->rtp_recvd) {
+				if(stream->video_rtcp_ctx[0] && stream->video_rtcp_ctx[0]->rtp_recvd) {
 					/* Create a RR */
 					int rrlen = 32;
 					char rtcpbuf[32];
@@ -3541,7 +3531,7 @@ void *janus_ice_send_thread(void *data) {
 					rr->header.type = RTCP_RR;
 					rr->header.rc = 1;
 					rr->header.length = htons((rrlen/4)-1);
-					janus_rtcp_report_block(stream->video_rtcp_ctx, &rr->rb[0]);
+					janus_rtcp_report_block(stream->video_rtcp_ctx[0], &rr->rb[0]);
 					/* Enqueue it, we'll send it later */
 					janus_ice_relay_rtcp_internal(handle, 1, rtcpbuf, 32, FALSE);
 				}
@@ -3551,7 +3541,7 @@ void *janus_ice_send_thread(void *data) {
 		/* Do the same with SR/SDES */
 		if(now-audio_rtcp_last_sr >= 5*G_USEC_PER_SEC) {
 			janus_ice_stream *stream = handle->audio_stream;
-			if(stream && stream->rtp_component && stream->rtp_component->out_stats.audio_packets > 0) {
+			if(stream && stream->rtp_component && stream->rtp_component->out_stats.audio.packets > 0) {
 				/* Create a SR/SDES compound */
 				int srlen = 28;
 				int sdeslen = 20;
@@ -3578,8 +3568,8 @@ void *janus_ice_send_thread(void *data) {
 					uint32_t rtp_ts = ((ntp-stream->audio_first_ntp_ts)/1000)*(rtcp_ctx->tb/1000) + stream->audio_first_rtp_ts;
 					sr->si.rtp_ts = htonl(rtp_ts);
 				}
-				sr->si.s_packets = htonl(stream->rtp_component->out_stats.audio_packets);
-				sr->si.s_octets = htonl(stream->rtp_component->out_stats.audio_bytes);
+				sr->si.s_packets = htonl(stream->rtp_component->out_stats.audio.packets);
+				sr->si.s_octets = htonl(stream->rtp_component->out_stats.audio.bytes);
 				rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[28];
 				janus_rtcp_sdes((char *)sdes, sdeslen, "janusaudio", 10);
 				/* Enqueue it, we'll send it later */
@@ -3589,7 +3579,7 @@ void *janus_ice_send_thread(void *data) {
 		}
 		if(now-video_rtcp_last_sr >= 5*G_USEC_PER_SEC) {
 			janus_ice_stream *stream = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? (handle->audio_stream ? handle->audio_stream : handle->video_stream) : (handle->video_stream);
-			if(stream && stream->rtp_component && stream->rtp_component->out_stats.video_packets > 0) {
+			if(stream && stream->rtp_component && stream->rtp_component->out_stats.video[0].packets > 0) {
 				/* Create a SR/SDES compound */
 				int srlen = 28;
 				int sdeslen = 20;
@@ -3608,16 +3598,16 @@ void *janus_ice_send_thread(void *data) {
 				sr->si.ntp_ts_msw = htonl(s);
 				sr->si.ntp_ts_lsw = htonl(f);
 				/* Compute an RTP timestamp coherent with the NTP one */
-				rtcp_context *rtcp_ctx = stream->video_rtcp_ctx;
+				rtcp_context *rtcp_ctx = stream->video_rtcp_ctx[0];
 				if(rtcp_ctx == NULL) {
 					sr->si.rtp_ts = htonl(stream->video_last_ts);	/* FIXME */
 				} else {
 					int64_t ntp = tv.tv_sec*G_USEC_PER_SEC + tv.tv_usec;
-					uint32_t rtp_ts = ((ntp-stream->video_first_ntp_ts)/1000)*(rtcp_ctx->tb/1000) + stream->video_first_rtp_ts;
+					uint32_t rtp_ts = ((ntp-stream->video_first_ntp_ts[0])/1000)*(rtcp_ctx->tb/1000) + stream->video_first_rtp_ts[0];
 					sr->si.rtp_ts = htonl(rtp_ts);
 				}
-				sr->si.s_packets = htonl(stream->rtp_component->out_stats.video_packets);
-				sr->si.s_octets = htonl(stream->rtp_component->out_stats.video_bytes);
+				sr->si.s_packets = htonl(stream->rtp_component->out_stats.video[0].packets);
+				sr->si.s_octets = htonl(stream->rtp_component->out_stats.video[0].bytes);
 				rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[28];
 				janus_rtcp_sdes((char *)sdes, sdeslen, "janusvideo", 10);
 				/* Enqueue it, we'll send it later */
@@ -3640,12 +3630,12 @@ void *janus_ice_send_thread(void *data) {
 					json_object_set_new(info, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
 					json_object_set_new(info, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
 					if(stream->rtp_component) {
-						json_object_set_new(info, "packets-received", json_integer(stream->rtp_component->in_stats.audio_packets));
-						json_object_set_new(info, "packets-sent", json_integer(stream->rtp_component->out_stats.audio_packets));
-						json_object_set_new(info, "bytes-received", json_integer(stream->rtp_component->in_stats.audio_bytes));
-						json_object_set_new(info, "bytes-sent", json_integer(stream->rtp_component->out_stats.audio_bytes));
-						json_object_set_new(info, "nacks-received", json_integer(stream->rtp_component->in_stats.audio_nacks));
-						json_object_set_new(info, "nacks-sent", json_integer(stream->rtp_component->out_stats.audio_nacks));
+						json_object_set_new(info, "packets-received", json_integer(stream->rtp_component->in_stats.audio.packets));
+						json_object_set_new(info, "packets-sent", json_integer(stream->rtp_component->out_stats.audio.packets));
+						json_object_set_new(info, "bytes-received", json_integer(stream->rtp_component->in_stats.audio.bytes));
+						json_object_set_new(info, "bytes-sent", json_integer(stream->rtp_component->out_stats.audio.bytes));
+						json_object_set_new(info, "nacks-received", json_integer(stream->rtp_component->in_stats.audio.nacks));
+						json_object_set_new(info, "nacks-sent", json_integer(stream->rtp_component->out_stats.audio.nacks));
 					}
 					janus_events_notify_handlers(JANUS_EVENT_TYPE_MEDIA, session->session_id, handle->handle_id, info);
 				}
@@ -3655,24 +3645,33 @@ void *janus_ice_send_thread(void *data) {
 		if(janus_ice_event_stats_period > 0 && now-video_last_event >= (gint64)janus_ice_event_stats_period*G_USEC_PER_SEC) {
 			if(janus_events_is_enabled() && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
 				janus_ice_stream *stream = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? (handle->audio_stream ? handle->audio_stream : handle->video_stream) : (handle->video_stream);
-				if(stream && stream->video_rtcp_ctx) {
-					json_t *info = json_object();
-					json_object_set_new(info, "media", json_string("video"));
-					json_object_set_new(info, "base", json_integer(stream->video_rtcp_ctx->tb));
-					json_object_set_new(info, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
-					json_object_set_new(info, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
-					json_object_set_new(info, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
-					json_object_set_new(info, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
-					json_object_set_new(info, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
-					if(stream->rtp_component) {
-						json_object_set_new(info, "packets-received", json_integer(stream->rtp_component->in_stats.video_packets));
-						json_object_set_new(info, "packets-sent", json_integer(stream->rtp_component->out_stats.video_packets));
-						json_object_set_new(info, "bytes-received", json_integer(stream->rtp_component->in_stats.video_bytes));
-						json_object_set_new(info, "bytes-sent", json_integer(stream->rtp_component->out_stats.video_bytes));
-						json_object_set_new(info, "nacks-received", json_integer(stream->rtp_component->in_stats.video_nacks));
-						json_object_set_new(info, "nacks-sent", json_integer(stream->rtp_component->out_stats.video_nacks));
+				int vindex=0;
+				for(vindex=0; vindex<3; vindex++) {
+					if(stream && stream->video_rtcp_ctx[vindex]) {
+						json_t *info = json_object();
+						if(vindex == 0)
+							json_object_set_new(info, "media", json_string("video"));
+						else if(vindex == 1)
+							json_object_set_new(info, "media", json_string("video-sim1"));
+						else
+							json_object_set_new(info, "media", json_string("video-sim2"));
+						json_object_set_new(info, "media", json_string("video"));
+						json_object_set_new(info, "base", json_integer(stream->video_rtcp_ctx[vindex]->tb));
+						json_object_set_new(info, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx[vindex])));
+						json_object_set_new(info, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], FALSE)));
+						json_object_set_new(info, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], TRUE)));
+						json_object_set_new(info, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], FALSE)));
+						json_object_set_new(info, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], TRUE)));
+						if(stream->rtp_component) {
+							json_object_set_new(info, "packets-received", json_integer(stream->rtp_component->in_stats.video[vindex].packets));
+							json_object_set_new(info, "packets-sent", json_integer(stream->rtp_component->out_stats.video[vindex].packets));
+							json_object_set_new(info, "bytes-received", json_integer(stream->rtp_component->in_stats.video[vindex].bytes));
+							json_object_set_new(info, "bytes-sent", json_integer(stream->rtp_component->out_stats.video[vindex].bytes));
+							json_object_set_new(info, "nacks-received", json_integer(stream->rtp_component->in_stats.video[vindex].nacks));
+							json_object_set_new(info, "nacks-sent", json_integer(stream->rtp_component->out_stats.video[vindex].nacks));
+						}
+						janus_events_notify_handlers(JANUS_EVENT_TYPE_MEDIA, session->session_id, handle->handle_id, info);
 					}
-					janus_events_notify_handlers(JANUS_EVENT_TYPE_MEDIA, session->session_id, handle->handle_id, info);
 				}
 			}
 			video_last_event = now;
@@ -3767,19 +3766,19 @@ void *janus_ice_send_thread(void *data) {
 					rr->header.rc = 0;
 					rr->header.length = htons((rrlen/4)-1);
 					janus_ice_stream *stream = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? (handle->audio_stream ? handle->audio_stream : handle->video_stream) : (handle->video_stream);
-					if(stream && stream->video_rtcp_ctx && stream->video_rtcp_ctx->rtp_recvd) {
+					if(stream && stream->video_rtcp_ctx[0] && stream->video_rtcp_ctx[0]->rtp_recvd) {
 						rr->header.rc = 1;
-						janus_rtcp_report_block(stream->video_rtcp_ctx, &rr->rb[0]);
+						janus_rtcp_report_block(stream->video_rtcp_ctx[0], &rr->rb[0]);
 					}
 					/* Append REMB */
 					memcpy(rtcpbuf+rrlen, pkt->data, pkt->length);
 					/* If we're simulcasting, set the extra SSRCs (the first one will be set by janus_rtcp_fix_ssrc) */
-					if(stream->video_ssrc_peer_sim_1 && pkt->length >= 28) {
+					if(stream->video_ssrc_peer[1] && pkt->length >= 28) {
 						rtcp_fb *rtcpfb = (rtcp_fb *)(rtcpbuf+rrlen);
 						rtcp_remb *remb = (rtcp_remb *)rtcpfb->fci;
-						remb->ssrc[1] = htonl(stream->video_ssrc_peer_sim_1);
-						if(stream->video_ssrc_peer_sim_2 && pkt->length >= 32) {
-							remb->ssrc[2] = htonl(stream->video_ssrc_peer_sim_2);
+						remb->ssrc[1] = htonl(stream->video_ssrc_peer[1]);
+						if(stream->video_ssrc_peer[2] && pkt->length >= 32) {
+							remb->ssrc[2] = htonl(stream->video_ssrc_peer[2]);
 						}
 					}
 					/* Free old packet and update */
@@ -3795,16 +3794,16 @@ void *janus_ice_send_thread(void *data) {
 				if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B)) {
 					JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Fixing SSRCs (local %u, peer %u)\n", handle->handle_id,
 						video ? stream->video_ssrc : stream->audio_ssrc,
-						video ? stream->video_ssrc_peer : stream->audio_ssrc_peer);
+						video ? stream->video_ssrc_peer[0] : stream->audio_ssrc_peer);
 					janus_rtcp_fix_ssrc(NULL, sbuf, pkt->length, 1,
 						video ? stream->video_ssrc : stream->audio_ssrc,
-						video ? stream->video_ssrc_peer : stream->audio_ssrc_peer);
+						video ? stream->video_ssrc_peer[0] : stream->audio_ssrc_peer);
 				} else {
 					/* Plan B involved, we trust the plugin to set the right 'local' SSRC and we don't mess with it */
 					JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Fixing peer SSRC (Plan B, peer %u)\n", handle->handle_id,
-						video ? stream->video_ssrc_peer : stream->audio_ssrc_peer);
+						video ? stream->video_ssrc_peer[0] : stream->audio_ssrc_peer);
 					janus_rtcp_fix_ssrc(NULL, sbuf, pkt->length, 1, 0,
-						video ? stream->video_ssrc_peer : stream->audio_ssrc_peer);
+						video ? stream->video_ssrc_peer[0] : stream->audio_ssrc_peer);
 				}
 				/* Do we need to dump this packet for debugging? */
 				if(g_atomic_int_get(&handle->dump_packets))
@@ -3928,8 +3927,8 @@ void *janus_ice_send_thread(void *data) {
 							janus_rtp_header *header = (janus_rtp_header *)sbuf;
 							guint32 timestamp = ntohl(header->timestamp);
 							if(pkt->type == JANUS_ICE_PACKET_AUDIO) {
-								component->out_stats.audio_packets++;
-								component->out_stats.audio_bytes += sent;
+								component->out_stats.audio.packets++;
+								component->out_stats.audio.bytes += sent;
 								stream->audio_last_ts = timestamp;
 								if(stream->audio_first_ntp_ts == 0) {
 									struct timeval tv;
@@ -3938,19 +3937,19 @@ void *janus_ice_send_thread(void *data) {
 									stream->audio_first_rtp_ts = timestamp;
 								}
 								/* Let's check if this was G.711: in case we may need to change the timestamp base */
-								rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx : stream->audio_rtcp_ctx;
+								rtcp_context *rtcp_ctx = video ? stream->video_rtcp_ctx[0] : stream->audio_rtcp_ctx;
 								int pt = header->type;
 								if((pt == 0 || pt == 8) && (rtcp_ctx->tb == 48000))
 									rtcp_ctx->tb = 8000;
 							} else if(pkt->type == JANUS_ICE_PACKET_VIDEO) {
-								component->out_stats.video_packets++;
-								component->out_stats.video_bytes += sent;
+								component->out_stats.video[0].packets++;
+								component->out_stats.video[0].bytes += sent;
 								stream->video_last_ts = timestamp;
-								if(stream->video_first_ntp_ts == 0) {
+								if(stream->video_first_ntp_ts[0] == 0) {
 									struct timeval tv;
 									gettimeofday(&tv, NULL);
-									stream->video_first_ntp_ts = (gint64)tv.tv_sec*G_USEC_PER_SEC + tv.tv_usec;
-									stream->video_first_rtp_ts = timestamp;
+									stream->video_first_ntp_ts[0] = (gint64)tv.tv_sec*G_USEC_PER_SEC + tv.tv_usec;
+									stream->video_first_rtp_ts[0] = timestamp;
 								}
 							}
 						}
diff --git a/ice.h b/ice.h
index f7f1aee..2b78063 100644
--- a/ice.h
+++ b/ice.h
@@ -188,31 +188,28 @@ typedef struct janus_ice_trickle janus_ice_trickle;
 
 /*! \brief Janus media statistics
  * \note To improve with more stuff */
+typedef struct janus_ice_stats_info {
+	/*! \brief Packets sent or received */
+	guint32 packets;
+	/*! \brief Bytes sent or received */
+	guint64 bytes;
+	/*! \brief Bytes sent or received in the last second */
+	GQueue *bytes_lastsec;
+	/*! \brief Whether or not we notified about lastsec issues already */
+	gboolean notified_lastsec;
+	/*! \brief Number of NACKs sent or received */
+	guint32 nacks;
+} janus_ice_stats_info;
+
+/*! \brief Janus media statistics container
+ * \note To improve with more stuff */
 typedef struct janus_ice_stats {
-	/*! \brief Audio packets sent or received */
-	guint32 audio_packets;
-	/*! \brief Audio bytes sent or received */
-	guint64 audio_bytes;
-	/*! \brief Audio bytes sent or received in the last second */
-	GQueue *audio_bytes_lastsec;
-	/*! \brief Whether or not we notified about audio lastsec issues already */
-	gboolean audio_notified_lastsec;
-	/*! \brief Number of audio NACKs sent or received */
-	guint32 audio_nacks;
-	/*! \brief Video packets sent or received */
-	guint32 video_packets;
-	/*! \brief Video bytes sent or received */
-	guint64 video_bytes;
-	/*! \brief Video bytes sent or received in the last second */
-	GQueue *video_bytes_lastsec;
-	/*! \brief Whether or not we notified about video lastsec issues already */
-	gboolean video_notified_lastsec;
-	/*! \brief Number of video NACKs sent or received */
-	guint32 video_nacks;
-	/*! \brief Data packets sent or received */
-	guint32 data_packets;
-	/*! \brief Data bytes sent or received */
-	guint64 data_bytes;
+	/*! \brief Audio info */
+	janus_ice_stats_info audio;
+	/*! \brief Video info (considering we may be simulcasting) */
+	janus_ice_stats_info video[3];
+	/*! \brief Data info */
+	janus_ice_stats_info data;
 	/*! \brief Last time the slow_link callback (of the plugin) was called */
 	gint64 last_slowlink_time;
 	/*! \brief Start time of recent NACKs (for slow_link) */
@@ -362,14 +359,10 @@ struct janus_ice_stream {
 	guint32 video_ssrc;
 	/*! \brief Audio SSRC of the peer for this stream (may be bundled) */
 	guint32 audio_ssrc_peer;
-	/*! \brief Video SSRC of the peer for this stream (may be bundled) */
-	guint32 video_ssrc_peer;
+	/*! \brief Video SSRC(s) of the peer for this stream (may be bundled, and simulcasting) */
+	guint32 video_ssrc_peer[3];
 	/*! \brief Video retransmissions SSRC of the peer for this stream (may be bundled) */
 	guint32 video_ssrc_peer_rtx;
-	/*! \brief Video SSRC (simulcasted 1) of the peer for this stream (may be bundled) */
-	guint32 video_ssrc_peer_sim_1;
-	/*! \brief Video SSRC (simulcasted 2) of the peer for this stream (may be bundled) */
-	guint32 video_ssrc_peer_sim_2;
 	/*! \brief Array of RTP Stream IDs (for Firefox simulcasting, if enabled) */
 	char *rid[3];
 	/*! \brief List of payload types we can expect for audio */
@@ -380,16 +373,16 @@ struct janus_ice_stream {
 	gint payload_type;
 	/*! \brief RTCP context for the audio stream (may be bundled) */
 	rtcp_context *audio_rtcp_ctx;
-	/*! \brief RTCP context for the video stream (may be bundled) */
-	rtcp_context *video_rtcp_ctx;
+	/*! \brief RTCP context(s) for the video stream (may be bundled, and simulcasting) */
+	rtcp_context *video_rtcp_ctx[3];
 	/*! \brief First received audio NTP timestamp */
 	gint64 audio_first_ntp_ts;
 	/*! \brief First received audio RTP timestamp */
 	guint32 audio_first_rtp_ts;
-	/*! \brief First received video NTP timestamp */
-	gint64 video_first_ntp_ts;
-	/*! \brief First received video NTP RTP timestamp */
-	guint32 video_first_rtp_ts;
+	/*! \brief First received video NTP timestamp (for all simulcast video streams) */
+	gint64 video_first_ntp_ts[3];
+	/*! \brief First received video NTP RTP timestamp (for all simulcast video streams) */
+	guint32 video_first_rtp_ts[3];
 	/*! \brief Last sent audio RTP timestamp */
 	guint32 audio_last_ts;
 	/*! \brief Last sent video RTP timestamp */
@@ -463,8 +456,8 @@ struct janus_ice_component {
 	guint nack_sent_recent_cnt;
 	/*! \brief List of recently received audio sequence numbers (as a support to NACK generation) */
 	janus_seq_info *last_seqs_audio;
-	/*! \brief List of recently received video sequence numbers (as a support to NACK generation) */
-	janus_seq_info *last_seqs_video;
+	/*! \brief List of recently received video sequence numbers (as a support to NACK generation, for each simulcast SSRC) */
+	janus_seq_info *last_seqs_video[3];
 	/*! \brief Stats for incoming data (audio/video/data) */
 	janus_ice_stats in_stats;
 	/*! \brief Stats for outgoing data (audio/video/data) */
diff --git a/janus.c b/janus.c
index 5de85dd..e507ae8 100644
--- a/janus.c
+++ b/janus.c
@@ -1130,10 +1130,10 @@ int janus_process_incoming_request(janus_request *request) {
 								if(handle->audio_stream->rtp_component && handle->video_stream->rtp_component)
 									handle->audio_stream->rtp_component->do_video_nacks = handle->video_stream->rtp_component->do_video_nacks;
 								handle->audio_stream->video_ssrc = handle->video_stream->video_ssrc;
-								handle->audio_stream->video_ssrc_peer = handle->video_stream->video_ssrc_peer;
+								handle->audio_stream->video_ssrc_peer[0] = handle->video_stream->video_ssrc_peer[0];
 								handle->audio_stream->video_ssrc_peer_rtx = handle->video_stream->video_ssrc_peer_rtx;
-								handle->audio_stream->video_ssrc_peer_sim_1 = handle->video_stream->video_ssrc_peer_sim_1;
-								handle->audio_stream->video_ssrc_peer_sim_2 = handle->video_stream->video_ssrc_peer_sim_2;
+								handle->audio_stream->video_ssrc_peer[1] = handle->video_stream->video_ssrc_peer[1];
+								handle->audio_stream->video_ssrc_peer[2] = handle->video_stream->video_ssrc_peer[2];
 								nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
 								if(!handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced())
 									nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
@@ -1153,9 +1153,13 @@ int janus_process_incoming_request(janus_request *request) {
 								if(handle->audio_stream->rtp_component)
 									handle->audio_stream->rtp_component->do_video_nacks = FALSE;
 								handle->audio_stream->video_ssrc = 0;
-								handle->audio_stream->video_ssrc_peer = 0;
-								g_free(handle->audio_stream->video_rtcp_ctx);
-								handle->audio_stream->video_rtcp_ctx = NULL;
+								handle->audio_stream->video_ssrc_peer[0] = 0;
+								g_free(handle->audio_stream->video_rtcp_ctx[0]);
+								handle->audio_stream->video_rtcp_ctx[0] = NULL;
+								g_free(handle->audio_stream->video_rtcp_ctx[1]);
+								handle->audio_stream->video_rtcp_ctx[1] = NULL;
+								g_free(handle->audio_stream->video_rtcp_ctx[2]);
+								handle->audio_stream->video_rtcp_ctx[2] = NULL;
 							}
 						} else if(video) {
 							/* Get rid of data, if present */
@@ -1367,19 +1371,19 @@ int janus_process_incoming_request(janus_request *request) {
 			body_jsep = json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped);
 			/* Check if VP8 simulcasting is enabled */
 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
-				if(handle->video_stream && handle->video_stream->video_ssrc_peer_sim_1) {
+				if(handle->video_stream && handle->video_stream->video_ssrc_peer[1]) {
 					json_t *simulcast = json_object();
-					json_object_set(simulcast, "ssrc-0", json_integer(handle->video_stream->video_ssrc_peer));
-					json_object_set(simulcast, "ssrc-1", json_integer(handle->video_stream->video_ssrc_peer_sim_1));
-					if(handle->video_stream->video_ssrc_peer_sim_2)
-						json_object_set(simulcast, "ssrc-2", json_integer(handle->video_stream->video_ssrc_peer_sim_2));
+					json_object_set(simulcast, "ssrc-0", json_integer(handle->video_stream->video_ssrc_peer[0]));
+					json_object_set(simulcast, "ssrc-1", json_integer(handle->video_stream->video_ssrc_peer[1]));
+					if(handle->video_stream->video_ssrc_peer[2])
+						json_object_set(simulcast, "ssrc-2", json_integer(handle->video_stream->video_ssrc_peer[2]));
 					json_object_set(body_jsep, "simulcast", simulcast);
-				} else if(handle->audio_stream && handle->audio_stream->video_ssrc_peer_sim_1) {
+				} else if(handle->audio_stream && handle->audio_stream->video_ssrc_peer[1]) {
 					json_t *simulcast = json_object();
-					json_object_set(simulcast, "ssrc-0", json_integer(handle->audio_stream->video_ssrc_peer));
-					json_object_set(simulcast, "ssrc-1", json_integer(handle->audio_stream->video_ssrc_peer_sim_1));
-					if(handle->audio_stream->video_ssrc_peer_sim_2)
-						json_object_set(simulcast, "ssrc-2", json_integer(handle->audio_stream->video_ssrc_peer_sim_2));
+					json_object_set(simulcast, "ssrc-0", json_integer(handle->audio_stream->video_ssrc_peer[0]));
+					json_object_set(simulcast, "ssrc-1", json_integer(handle->audio_stream->video_ssrc_peer[1]));
+					if(handle->audio_stream->video_ssrc_peer[2])
+						json_object_set(simulcast, "ssrc-2", json_integer(handle->audio_stream->video_ssrc_peer[2]));
 					json_object_set(body_jsep, "simulcast", simulcast);
 				}
 			}
@@ -2440,14 +2444,14 @@ json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
 		json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
 	if(stream->audio_ssrc_peer)
 		json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
-	if(stream->video_ssrc_peer)
-		json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
+	if(stream->video_ssrc_peer[0])
+		json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer[0]));
 	if(stream->video_ssrc_peer_rtx)
 		json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
-	if(stream->video_ssrc_peer_sim_1)
-		json_object_set_new(ss, "video-peer-sim-1", json_integer(stream->video_ssrc_peer_sim_1));
-	if(stream->video_ssrc_peer_sim_2)
-		json_object_set_new(ss, "video-peer-sim-2", json_integer(stream->video_ssrc_peer_sim_2));
+	if(stream->video_ssrc_peer[1])
+		json_object_set_new(ss, "video-peer-sim-1", json_integer(stream->video_ssrc_peer[1]));
+	if(stream->video_ssrc_peer[2])
+		json_object_set_new(ss, "video-peer-sim-2", json_integer(stream->video_ssrc_peer[2]));
 	if(stream->rid[0]) {
 		json_t *rid = json_array();
 		json_array_append_new(rid, json_string(stream->rid[0]));
@@ -2481,17 +2485,25 @@ json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
 		json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
 		json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
 	}
-	if(stream->video_rtcp_ctx != NULL) {
-		if(rtcp_stats == NULL)
-			rtcp_stats = json_object();
-		json_t *video_rtcp_stats = json_object();
-		json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx->tb));
-		json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
-		json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
-		json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
-		json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
-		json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
-		json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
+	int vindex=0;
+	for(vindex=0; vindex<3; vindex++) {
+		if(stream->video_rtcp_ctx[vindex] != NULL) {
+			if(rtcp_stats == NULL)
+				rtcp_stats = json_object();
+			json_t *video_rtcp_stats = json_object();
+			json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx[vindex]->tb));
+			json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx[vindex])));
+			json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], FALSE)));
+			json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], TRUE)));
+			json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], FALSE)));
+			json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], TRUE)));
+			if(vindex == 0)
+				json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
+			else if(vindex == 1)
+				json_object_set_new(rtcp_stats, "video-sim1", video_rtcp_stats);
+			else
+				json_object_set_new(rtcp_stats, "video-sim2", video_rtcp_stats);
+		}
 	}
 	if(rtcp_stats != NULL)
 		json_object_set_new(s, "rtcp_stats", rtcp_stats);
@@ -2553,16 +2565,16 @@ json_t *janus_admin_component_summary(janus_ice_component *component) {
 		if(dtls->dtls_connected > 0)
 			json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
-			json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio_packets));
-			json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio_bytes));
+			json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio.packets));
+			json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio.bytes));
 			json_object_set_new(in_stats, "do_audio_nacks", component->do_audio_nacks ? json_true() : json_false());
 			if(component->do_audio_nacks)
-				json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio_nacks));
+				json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio.nacks));
 			/* Compute the last second stuff too */
 			gint64 now = janus_get_monotonic_time();
 			guint64 bytes = 0;
-			if(component->in_stats.audio_bytes_lastsec) {
-				GList *lastsec = g_queue_peek_head_link(component->in_stats.audio_bytes_lastsec);
+			if(component->in_stats.audio.bytes_lastsec) {
+				GList *lastsec = g_queue_peek_head_link(component->in_stats.audio.bytes_lastsec);
 				while(lastsec) {
 					janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
 					if(s && now-s->when < G_USEC_PER_SEC)
@@ -2573,39 +2585,50 @@ json_t *janus_admin_component_summary(janus_ice_component *component) {
 			json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(bytes));
 		}
 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
-			json_object_set_new(in_stats, "video_packets", json_integer(component->in_stats.video_packets));
-			json_object_set_new(in_stats, "video_bytes", json_integer(component->in_stats.video_bytes));
-			json_object_set_new(in_stats, "do_video_nacks", component->do_video_nacks ? json_true() : json_false());
-			if(component->do_video_nacks)
-				json_object_set_new(in_stats, "video_nacks", json_integer(component->in_stats.video_nacks));
-			/* Compute the last second stuff too */
-			gint64 now = janus_get_monotonic_time();
-			guint64 bytes = 0;
-			if(component->in_stats.video_bytes_lastsec) {
-				GList *lastsec = g_queue_peek_head_link(component->in_stats.video_bytes_lastsec);
-				while(lastsec) {
-					janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
-					if(s && now-s->when < G_USEC_PER_SEC)
-						bytes += s->bytes;
-					lastsec = lastsec->next;
+			int vindex=0;
+			for(vindex=0; vindex<3; vindex++) {
+				if(vindex > 0 && component->stream->video_ssrc_peer[vindex] == 0)
+					continue;
+				json_t *container = (vindex == 0 ? in_stats : json_object());
+				json_object_set_new(container, "video_packets", json_integer(component->in_stats.video[vindex].packets));
+				json_object_set_new(container, "video_bytes", json_integer(component->in_stats.video[vindex].bytes));
+				if(vindex == 0)
+					json_object_set_new(container, "do_video_nacks", component->do_video_nacks ? json_true() : json_false());
+				if(component->do_video_nacks)
+					json_object_set_new(container, "video_nacks", json_integer(component->in_stats.video[vindex].nacks));
+				/* Compute the last second stuff too */
+				gint64 now = janus_get_monotonic_time();
+				guint64 bytes = 0;
+				if(component->in_stats.video[vindex].bytes_lastsec) {
+					GList *lastsec = g_queue_peek_head_link(component->in_stats.video[vindex].bytes_lastsec);
+					while(lastsec) {
+						janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
+						if(s && now-s->when < G_USEC_PER_SEC)
+							bytes += s->bytes;
+						lastsec = lastsec->next;
+					}
 				}
+				json_object_set_new(container, "video_bytes_lastsec", json_integer(bytes));
+				if(vindex == 1)
+					json_object_set_new(in_stats, "video-simulcast-1", container);
+				else if(vindex == 2)
+					json_object_set_new(in_stats, "video-simulcast-2", container);
 			}
-			json_object_set_new(in_stats, "video_bytes_lastsec", json_integer(bytes));
 		}
-		json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data_packets));
-		json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data_bytes));
+		json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data.packets));
+		json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data.bytes));
 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
-			json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio_packets));
-			json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio_bytes));
-			json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio_nacks));
+			json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio.packets));
+			json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio.bytes));
+			json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio.nacks));
 		}
 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
-			json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video_packets));
-			json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video_bytes));
-			json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video_nacks));
+			json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video[0].packets));
+			json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video[0].bytes));
+			json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video[0].nacks));
 		}
-		json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data_packets));
-		json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data_bytes));
+		json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data.packets));
+		json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data.bytes));
 #ifdef HAVE_SCTP
 		/* FIXME Actually check if this succeeded? */
 		json_object_set_new(d, "sctp-association", dtls->sctp ? json_true() : json_false());
@@ -3000,10 +3023,10 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
 						if(ice_handle->audio_stream->rtp_component && ice_handle->video_stream->rtp_component)
 							ice_handle->audio_stream->rtp_component->do_video_nacks = ice_handle->video_stream->rtp_component->do_video_nacks;
 						ice_handle->audio_stream->video_ssrc = ice_handle->video_stream->video_ssrc;
-						ice_handle->audio_stream->video_ssrc_peer = ice_handle->video_stream->video_ssrc_peer;
+						ice_handle->audio_stream->video_ssrc_peer[0] = ice_handle->video_stream->video_ssrc_peer[0];
 						ice_handle->audio_stream->video_ssrc_peer_rtx = ice_handle->video_stream->video_ssrc_peer_rtx;
-						ice_handle->audio_stream->video_ssrc_peer_sim_1 = ice_handle->video_stream->video_ssrc_peer_sim_1;
-						ice_handle->audio_stream->video_ssrc_peer_sim_2 = ice_handle->video_stream->video_ssrc_peer_sim_2;
+						ice_handle->audio_stream->video_ssrc_peer[1] = ice_handle->video_stream->video_ssrc_peer[1];
+						ice_handle->audio_stream->video_ssrc_peer[2] = ice_handle->video_stream->video_ssrc_peer[2];
 						nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
 						if(!ice_handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced())
 							nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
@@ -3023,9 +3046,13 @@ json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plug
 						if(ice_handle->audio_stream->rtp_component)
 							ice_handle->audio_stream->rtp_component->do_video_nacks = FALSE;
 						ice_handle->audio_stream->video_ssrc = 0;
-						ice_handle->audio_stream->video_ssrc_peer = 0;
-						g_free(ice_handle->audio_stream->video_rtcp_ctx);
-						ice_handle->audio_stream->video_rtcp_ctx = NULL;
+						ice_handle->audio_stream->video_ssrc_peer[0] = 0;
+						g_free(ice_handle->audio_stream->video_rtcp_ctx[0]);
+						ice_handle->audio_stream->video_rtcp_ctx[0] = NULL;
+						g_free(ice_handle->audio_stream->video_rtcp_ctx[1]);
+						ice_handle->audio_stream->video_rtcp_ctx[1] = NULL;
+						g_free(ice_handle->audio_stream->video_rtcp_ctx[2]);
+						ice_handle->audio_stream->video_rtcp_ctx[2] = NULL;
 					}
 				} else if(video) {
 					/* Get rid of data, if present */
diff --git a/rtcp.c b/rtcp.c
index 5507145..17fe671 100644
--- a/rtcp.c
+++ b/rtcp.c
@@ -162,8 +162,8 @@ int janus_rtcp_fix_ssrc(rtcp_context *ctx, char *packet, int len, int fixssrc, u
 			case RTCP_SR: {
 				/* SR, sender report */
 				JANUS_LOG(LOG_HUGE, "     #%d SR (200)\n", pno);
-				rtcp_sr *sr = (rtcp_sr*)rtcp;
-				/* RTCP context provided, update it with info on this SR */
+				rtcp_sr *sr = (rtcp_sr *)rtcp;
+				/* If an RTCP context was provided, update it with info on this SR */
 				janus_rtcp_incoming_sr(ctx, sr);
 				if(fixssrc && newssrcl) {
 					sr->ssrc = htonl(newssrcl);
@@ -176,11 +176,9 @@ int janus_rtcp_fix_ssrc(rtcp_context *ctx, char *packet, int len, int fixssrc, u
 			case RTCP_RR: {
 				/* RR, receiver report */
 				JANUS_LOG(LOG_HUGE, "     #%d RR (201)\n", pno);
-				rtcp_rr *rr = (rtcp_rr*)rtcp;
-				if(ctx != NULL) {
-					/* RTCP context provided, update it with info on this SR */
-					janus_rtcp_incoming_rr(ctx, rr);
-				}
+				rtcp_rr *rr = (rtcp_rr *)rtcp;
+				/* If an RTCP context was provided, update it with info on this RR */
+				janus_rtcp_incoming_rr(ctx, rr);
 				if(fixssrc && newssrcl) {
 					rr->ssrc = htonl(newssrcl);
 				}
diff --git a/sdp.c b/sdp.c
index 1f13d91..d8de5d4 100644
--- a/sdp.c
+++ b/sdp.c
@@ -351,6 +351,14 @@ int janus_sdp_process(void *ice_handle, janus_sdp *remote_sdp) {
 			}
 			tempA = tempA->next;
 		}
+		if(stream->video_ssrc_peer[1] && stream->video_rtcp_ctx[1] == NULL) {
+			stream->video_rtcp_ctx[1] = g_malloc0(sizeof(rtcp_context));
+			stream->video_rtcp_ctx[1]->tb = 90000;
+		}
+		if(stream->video_ssrc_peer[2] && stream->video_rtcp_ctx[2] == NULL) {
+			stream->video_rtcp_ctx[2] = g_malloc0(sizeof(rtcp_context));
+			stream->video_rtcp_ctx[2]->tb = 90000;
+		}
 		temp = temp->next;
 	}
 	if(ruser)
@@ -605,16 +613,16 @@ int janus_sdp_parse_ssrc_group(void *ice_stream, const char *group_attr, int vid
 				ssrc = g_ascii_strtoull(index, NULL, 0);
 				switch(i) {
 					case 1:
-						stream->video_ssrc_peer = ssrc;
-						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer);
+						stream->video_ssrc_peer[0] = ssrc;
+						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[0]);
 						break;
 					case 2:
 						if(fid) {
 							stream->video_ssrc_peer_rtx = ssrc;
 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx);
 						} else if(sim) {
-							stream->video_ssrc_peer_sim_1 = ssrc;
-							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_sim_1);
+							stream->video_ssrc_peer[1] = ssrc;
+							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[1]);
 						} else {
 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
 						}
@@ -623,8 +631,8 @@ int janus_sdp_parse_ssrc_group(void *ice_stream, const char *group_attr, int vid
 						if(fid) {
 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Found one too many retransmission SSRC (rtx): %"SCNu64"\n", handle->handle_id, ssrc);
 						} else if(sim) {
-							stream->video_ssrc_peer_sim_2 = ssrc;
-							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_sim_2);
+							stream->video_ssrc_peer[2] = ssrc;
+							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[2]);
 						} else {
 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
 						}
@@ -654,17 +662,17 @@ int janus_sdp_parse_ssrc(void *ice_stream, const char *ssrc_attr, int video) {
 		return -3;
 	if(video) {
 		if(stream->video_ssrc_peer == 0) {
-			stream->video_ssrc_peer = ssrc;
-			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer);
+			stream->video_ssrc_peer[0] = ssrc;
+			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[0]);
 		} else {
 			/* We already have a video SSRC: check if RID is involved, and we'll keep track of this for simulcasting */
 			if(stream->rid[0]) {
-				if(stream->video_ssrc_peer_sim_1 == 0) {
-					stream->video_ssrc_peer_sim_1 = ssrc;
-					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_sim_1);
-				} else if(stream->video_ssrc_peer_sim_2 == 0) {
-					stream->video_ssrc_peer_sim_2 = ssrc;
-					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_sim_2);
+				if(stream->video_ssrc_peer[1] == 0) {
+					stream->video_ssrc_peer[1] = ssrc;
+					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[1]);
+				} else if(stream->video_ssrc_peer[2] == 0) {
+					stream->video_ssrc_peer[2] = ssrc;
+					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer[2]);
 				} else {
 					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with video SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
 				}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/janus.git



More information about the Pkg-voip-commits mailing list