[Pkg-voip-commits] [janus] 08/163: Several simulcast related fixes (third substream, REMB, etc.)

Jonas Smedegaard dr at jones.dk
Sat Oct 28 01:22:03 UTC 2017


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

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

commit 460a64319542c49620d3a056d7188dc85cd46bc0
Merge: 3e01d6e cad85c9
Author: Lorenzo Miniero <lminiero at gmail.com>
Date:   Wed Jun 28 20:34:24 2017 +0200

    Several simulcast related fixes (third substream, REMB, etc.)

 dtls.c                   |  23 +++---
 html/admin.js            | 199 ++++++++++++++++++++++++++++++++++++++++++-----
 html/devicetest.html     |   5 +-
 html/devicetest.js       |  27 +++++--
 html/echotest.html       |   5 +-
 html/echotest.js         |  28 ++++++-
 html/janus.js            | 124 ++++++++++-------------------
 html/janus.nojquery.js   | 108 +++++++------------------
 html/siptest.js          |  21 ++++-
 html/videocalltest.js    |   3 +-
 html/videoroomtest.js    |   3 +-
 ice.c                    | 170 +++++++++++++++++++++++++++++++---------
 ice.h                    |   6 +-
 janus.c                  |   4 +
 plugins/janus_echotest.c |  85 ++++++++++++--------
 plugins/janus_sip.c      | 148 ++++++++++++++++++++++-------------
 rtcp.c                   |  25 ++++--
 rtcp.h                   |  12 ++-
 sdp-utils.c              |   2 -
 19 files changed, 661 insertions(+), 337 deletions(-)

diff --cc html/devicetest.html
index 798a54c,8872892..de54a53
--- a/html/devicetest.html
+++ b/html/devicetest.html
@@@ -120,12 -118,6 +120,13 @@@
  								<h3 class="panel-title">Remote Stream
  									<span class="label label-primary hide" id="curres"></span>
  									<span class="label label-info hide" id="curbitrate"></span>
 +									<div id="simulcast" class="btn-group btn-group-xs pull-right hide">
 +										<div class="btn-group btn-group-xs">
- 											<button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to higher quality" style="width: 50%">SL 1</button>
- 											<button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to lower quality" style="width: 50%">SL 0</button>
++											<button id="sl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to higher quality" style="width: 33%">SL 2</button>
++											<button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to normal quality" style="width: 33%">SL 1</button>
++											<button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to lower quality" style="width: 34%">SL 0</button>
 +										</div>
 +									</div>
  									<div class="btn-group btn-group-xs pull-right hide" id="output-devices">
  										<div class="btn-group btn-group-xs">
  											<button id="outputdeviceset" autocomplete="off" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
diff --cc html/devicetest.js
index 07f5f8c,9d0ab12..9383910
--- a/html/devicetest.js
+++ b/html/devicetest.js
@@@ -267,39 -261,6 +265,56 @@@ $(document).ready(function() 
  											$('#outputdeviceset').html('Output device<span class="caret"></span>');
  										}
  									}
 +									// Is simulcast in place?
 +									var simulcast = msg["simulcast"];
 +									if(simulcast !== null && simulcast !== undefined) {
 +										if(!simulcastStarted) {
 +											simulcastStarted = true;
 +											$('#simulcast').removeClass('hide');
 +											// Enable the VP8 simulcast selection buttons
 +											$('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
 +												.unbind('click').click(function() {
 +													toastr.info("Switching simulcast video, wait for it... (lower quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
 +													echotest.send({message: { simulcast: 0}});
 +												});
 +											$('#sl-1').removeClass('btn-primary btn-success').addClass('btn-success')
 +												.unbind('click').click(function() {
- 													toastr.info("Switching simulcast video, wait for it... (higher quality)", null, {timeOut: 2000});
++													toastr.info("Switching simulcast video, wait for it... (normal quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
 +													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													echotest.send({message: { simulcast: 1}});
 +												});
++											$('#sl-2').removeClass('btn-primary btn-success').addClass('btn-success')
++												.unbind('click').click(function() {
++													toastr.info("Switching simulcast video, wait for it... (higher quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
++													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++													echotest.send({message: { simulcast: 1}});
++												});
 +										}
 +										// We just received notice that there's been a switch, update the buttons
 +										if(simulcast === 0) {
 +											toastr.success("Switched simulcast video! (lower quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
 +										} else if(simulcast === 1) {
- 											toastr.success("Switched simulcast video! (higher quality)", null, {timeOut: 2000});
++											toastr.success("Switched simulcast video! (normal quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
 +											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++										} else if(simulcast === 2) {
++											toastr.success("Switched simulcast video! (higher quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
++											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +										}
 +									}
  								},
  								onlocalstream: function(stream) {
  									Janus.debug(" ::: Got a local stream :::");
@@@ -449,10 -411,6 +465,11 @@@
  									$('#datasend').val('').attr('disabled', true);
  									$('#datarecv').val('');
  									$('#outputdeviceset').html('Output device<span class="caret"></span>');
 +									simulcastStarted = false;
 +									$('#simulcast').addClass('hide');
 +									$('#sl-0').unbind('click');
 +									$('#sl-1').unbind('click');
++									$('#sl-2').unbind('click');
  								}
  							});
  					},
diff --cc html/echotest.html
index f9e576a,8308399..8542315
--- a/html/echotest.html
+++ b/html/echotest.html
@@@ -109,14 -104,7 +109,15 @@@
  					<div class="col-md-6">
  						<div class="panel panel-default">
  							<div class="panel-heading">
 -								<h3 class="panel-title">Remote Stream <span class="label label-primary hide" id="curres"></span> <span class="label label-info hide" id="curbitrate"></span></h3>
 +								<h3 class="panel-title">Remote Stream <span class="label label-primary hide" id="curres"></span> <span class="label label-info hide" id="curbitrate"></span>
 +									<div id="simulcast" class="btn-group btn-group-xs pull-right hide">
 +										<div class="btn-group btn-group-xs">
- 											<button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to higher quality" style="width: 50%">SL 1</button>
- 											<button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to lower quality" style="width: 50%">SL 0</button>
++											<button id="sl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to higher quality" style="width: 33%">SL 2</button>
++											<button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to normal quality" style="width: 33%">SL 1</button>
++											<button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to lower quality" style="width: 34%">SL 0</button>
 +										</div>
 +									</div>
 +								</h3>
  							</div>
  							<div class="panel-body" id="videoright"></div>
  						</div>
diff --cc html/echotest.js
index c98b866,1e2201e..9d53c6d
--- a/html/echotest.js
+++ b/html/echotest.js
@@@ -189,39 -185,6 +189,56 @@@ $(document).ready(function() 
  											$('#curres').hide();
  										}
  									}
 +									// Is simulcast in place?
 +									var simulcast = msg["simulcast"];
 +									if(simulcast !== null && simulcast !== undefined) {
 +										if(!simulcastStarted) {
 +											simulcastStarted = true;
 +											$('#simulcast').removeClass('hide');
 +											// Enable the VP8 simulcast selection buttons
 +											$('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
 +												.unbind('click').click(function() {
 +													toastr.info("Switching simulcast video, wait for it... (lower quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
 +													echotest.send({message: { simulcast: 0}});
 +												});
 +											$('#sl-1').removeClass('btn-primary btn-success').addClass('btn-success')
 +												.unbind('click').click(function() {
- 													toastr.info("Switching simulcast video, wait for it... (higher quality)", null, {timeOut: 2000});
++													toastr.info("Switching simulcast video, wait for it... (normal quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
 +													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +													echotest.send({message: { simulcast: 1}});
 +												});
++											$('#sl-2').removeClass('btn-primary btn-success').addClass('btn-success')
++												.unbind('click').click(function() {
++													toastr.info("Switching simulcast video, wait for it... (higher quality)", null, {timeOut: 2000});
++													$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
++													$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++													$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++													echotest.send({message: { simulcast: 2}});
++												});
 +										}
 +										// We just received notice that there's been a switch, update the buttons
 +										if(simulcast === 0) {
 +											toastr.success("Switched simulcast video! (lower quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
 +										} else if(simulcast === 1) {
- 											toastr.success("Switched simulcast video! (higher quality)", null, {timeOut: 2000});
++											toastr.success("Switched simulcast video! (normal quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
 +											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++										} else if(simulcast === 2) {
++											toastr.success("Switched simulcast video! (higher quality)", null, {timeOut: 2000});
++											$('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
++											$('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
++											$('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
 +										}
 +									}
  								},
  								onlocalstream: function(stream) {
  									Janus.debug(" ::: Got a local stream :::");
@@@ -361,6 -325,6 +379,10 @@@
  									$('#curbitrate').hide();
  									$('#curres').hide();
  									$('#datasend').attr('disabled', true);
++									$('#simulcast').addClass('hide');
++									$('#sl-0').unbind('click');
++									$('#sl-1').unbind('click');
++									$('#sl-2').unbind('click');
  								}
  							});
  					},
diff --cc html/janus.js
index 46d1e54,5f642e2..79e512e
--- a/html/janus.js
+++ b/html/janus.js
@@@ -1402,6 -1403,6 +1403,9 @@@ function Janus(gatewayCallbacks) 
  			}
  			var videoSupport = isVideoSendEnabled(media);
  			if(videoSupport === true && media != undefined && media != null) {
++				var simulcast = callbacks.simulcast === true ? true : false;
++				if(simulcast && !jsep && (media.video === undefined || media.video === false))
++					media.video = "hires";
  				if(media.video && media.video != 'screen' && media.video != 'window') {
  					var width = 0;
  					var height = 0, maxHeight = 0;
@@@ -1731,8 -1732,7 +1735,12 @@@
  			return;
  		}
  		var config = pluginHandle.webrtcStuff;
 -		Janus.log("Creating offer (iceDone=" + config.iceDone + ")");
 +		var simulcast = callbacks.simulcast === true ? true : false;
- 		Janus.log("Creating offer (iceDone=" + config.iceDone + ", simulcast=" + simulcast + ")");
++		if(!simulcast) {
++			Janus.log("Creating offer (iceDone=" + config.iceDone + ")");
++		} else {
++			Janus.log("Creating offer (iceDone=" + config.iceDone + ", simulcast=" + simulcast + ")");
++		}
  		// https://code.google.com/p/webrtc/issues/detail?id=3508
  		var mediaConstraints = null;
  		if(adapter.browserDetails.browser == "firefox" || adapter.browserDetails.browser == "edge") {
@@@ -1749,129 -1749,11 +1757,129 @@@
  			};
  		}
  		Janus.debug(mediaConstraints);
 +		// Check if this is Firefox and we've been asked to do simulcasting
 +		if(simulcast && adapter.browserDetails.browser === "firefox") {
 +			// FIXME Based on https://gist.github.com/voluntas/088bc3cc62094730647b
 +			Janus.log("Enabling Simulcasting for Firefox (RID)");
 +			var sender = config.pc.getSenders()[1];
 +			Janus.log(sender);
 +			var parameters = sender.getParameters();
 +			Janus.log(parameters);
 +			sender.setParameters({encodings: [
 +				{ rid: "high", active: true, priority: "high", maxBitrate: 2000000 },
- 				{ rid: "medium", active: true, priority: "medium", maxBitrate: 200000 }
++				{ rid: "medium", active: true, priority: "medium", maxBitrate: 400000 },
++				{ rid: "low", active: true, priority: "medium", maxBitrate: 200000 }
 +			]});
 +		}
  		config.pc.createOffer(
  			function(offer) {
  				Janus.debug(offer);
  				if(config.mySdp === null || config.mySdp === undefined) {
  					Janus.log("Setting local description");
 +					if(simulcast) {
 +						// This SDP munging only works with Chrome
 +						if(adapter.browserDetails.browser === "chrome") {
 +							Janus.log("Enabling Simulcasting for Chrome (SDP munging)");
 +							// Let's munge the SDP to add the attributes for enabling simulcasting
 +							// (based on https://gist.github.com/ggarber/a19b4c33510028b9c657)
 +							var lines = offer.sdp.split("\r\n");
 +							var video = false;
 +							var ssrc = [ -1 ], ssrc_fid = -1;
 +							var cname = null, msid = null, mslabel = null, label = null;
 +							var insertAt = -1;
 +							for(var i=0; i<lines.length; i++) {
 +								var mline = lines[i].match(/m=(\w+) */);
 +								if(mline) {
 +									var medium = mline[1];
 +									if(medium === "video") {
 +										// New video m-line: make sure it's the first one
 +										if(ssrc[0] < 0) {
 +											video = true;
 +										} else {
 +											// We're done, let's add the new attributes here
 +											insertAt = i;
 +											break;
 +										}
 +									} else {
 +										// New non-video m-line: do we have what we were looking for?
 +										if(ssrc[0] > -1) {
 +											// We're done, let's add the new attributes here
 +											insertAt = i;
 +											break;
 +										}
 +									}
 +									continue;
 +								}
 +								var fid = lines[i].match(/a=ssrc-group:FID (\d+) (\d+)/);
 +								if(fid) {
 +									ssrc[0] = fid[1];
 +									ssrc_fid = fid[2];
 +									lines.splice(i, 1); i--;
 +									continue;
 +								}
 +								if(ssrc[0]) {
 +									var match = lines[i].match('a=ssrc:' + ssrc[0] + ' cname:(.+)')
 +									if(match) {
 +										cname = match[1];
 +									}
 +									match = lines[i].match('a=ssrc:' + ssrc[0] + ' msid:(.+)')
 +									if(match) {
 +										msid = match[1];
 +									}
 +									match = lines[i].match('a=ssrc:' + ssrc[0] + ' mslabel:(.+)')
 +									if(match) {
 +										mslabel = match[1];
 +									}
 +									match = lines[i].match('a=ssrc:' + ssrc + ' label:(.+)')
 +									if(match) {
 +										label = match[1];
 +									}
 +									if(lines[i].indexOf('a=ssrc:' + ssrc_fid) === 0) {
 +										lines.splice(i, 1); i--;
 +										continue;
 +									}
 +									if(lines[i].indexOf('a=ssrc:' + ssrc[0]) === 0) {
 +										lines.splice(i, 1); i--;
 +										continue;
 +									}
 +								}
 +								if(lines[i].length == 0) {
 +									lines.splice(i, 1); i--;
 +									continue;
 +								}
 +							}
 +							if(insertAt < 0) {
 +								// Append at the end
 +								insertAt = lines.length-1;
 +							}
 +							// Generate a couple of SSRCs
 +							ssrc[1] = Math.floor(Math.random()*0xFFFFFFFF);
 +							ssrc[2] = Math.floor(Math.random()*0xFFFFFFFF);
 +							// Add attributes to the SDP
- 							lines.splice(insertAt, 0, 'a=ssrc-group:SIM ' + ssrc[0] + ' ' + ssrc[1] + ' ' + ssrc[2]);
- 							insertAt++;
 +							for(var i=0; i<ssrc.length; i++) {
 +								if(cname) {
 +									lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' cname:' + cname);
 +									insertAt++;
 +								}
 +								if(msid) {
 +									lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' msid:' + msid);
 +									insertAt++;
 +								}
 +								if(mslabel) {
 +									lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' mslabel:' + msid);
 +									insertAt++;
 +								}
 +								if(label) {
 +									lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' label:' + msid);
 +									insertAt++;
 +								}
 +							}
++							lines.splice(insertAt, 0, 'a=ssrc-group:SIM ' + ssrc[0] + ' ' + ssrc[1] + ' ' + ssrc[2]);
 +							offer.sdp = lines.join("\r\n");
 +						} else if(adapter.browserDetails.browser !== "firefox") {
 +							Janus.warn("simulcast=true, but this is not Chrome nor Firefox, ignoring");
 +						}
 +					}
  					config.mySdp = offer.sdp;
  					config.pc.setLocalDescription(offer);
  				}
diff --cc ice.c
index 49afd90,600be4f..141f810
--- a/ice.c
+++ b/ice.c
@@@ -2193,7 -2241,7 +2260,19 @@@ void janus_ice_cb_nice_recv(NiceAgent *
  						} else {
  							/* Check the remote SSRC, compare it to what we have */
  							guint32 rtcp_ssrc = janus_rtcp_get_sender_ssrc(buf, len);
--							video = (stream->video_ssrc_peer == rtcp_ssrc ? 1 : 0);
++							if(rtcp_ssrc == stream->audio_ssrc_peer) {
++								video = 0;
++							} else if(rtcp_ssrc == stream->video_ssrc_peer) {
++								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_1 && rtcp_ssrc == stream->video_ssrc_peer_sim_1)) {
++									/* 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;
++								}
++							}
  							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);
  						}
@@@ -3601,6 -3654,6 +3690,17 @@@ void *janus_ice_send_thread(void *data
  					}
  					/* 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) {
++						rtcp_fb *rtcpfb = (rtcp_fb *)(rtcpbuf+rrlen);
++						rtcp_remb *remb = (rtcp_remb *)rtcpfb->fci;
++						remb->ssrc[1] = htonl(stream->video_ssrc_peer_sim_1);
++						JANUS_LOG(LOG_WARN, "Setting SSRC #2 in REMB: %"SCNu32"\n", 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);
++							JANUS_LOG(LOG_WARN, "Setting SSRC #3 in REMB: %"SCNu32"\n", stream->video_ssrc_peer_sim_2);
++						}
++					}
  					/* Free old packet and update */
  					char *prev_data = pkt->data;
  					pkt->data = rtcpbuf;
diff --cc plugins/janus_echotest.c
index 34ea725,7fbb3d1..dd36c1e
--- a/plugins/janus_echotest.c
+++ b/plugins/janus_echotest.c
@@@ -449,9 -434,7 +449,12 @@@ json_t *janus_echotest_query_session(ja
  	json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
  	json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
  	json_object_set_new(info, "bitrate", json_integer(session->bitrate));
- 	if(session->ssrc[0] != 0)
 -	if(session->arc || session->vrc || session->drc) {
++	if(session->ssrc[0] != 0) {
 +		json_object_set_new(info, "simulcast", json_true());
++		json_object_set_new(info, "simulcast-level", json_integer(session->simulcast));
++		json_object_set_new(info, "simulcast-target", json_integer(session->simulcast_target));
++	}
 +	if(session->arc || session->vrc[0] || session->drc) {
  		json_t *recording = json_object();
  		if(session->arc && session->arc->filename)
  			json_object_set_new(recording, "audio", json_string(session->arc->filename));
@@@ -515,74 -494,11 +518,77 @@@ void janus_echotest_incoming_rtp(janus_
  		}
  		if(session->destroyed)
  			return;
 -		if((!video && session->audio_active) || (video && session->video_active)) {
 +		if(video && session->video_active && session->rtpmapid_extmap_id != -1) {
 +			/* FIXME Experiments with Firefox simulcasting */
 +			rtp_header *header = (rtp_header *)buf;
 +			uint32_t seq_number = ntohs(header->seq_number);
 +			uint32_t timestamp = ntohl(header->timestamp);
 +			uint32_t ssrc = ntohl(header->ssrc);
 +			char sdes_item[16];
 +			if(janus_rtp_header_extension_parse_rtp_stream_id(buf, len, session->rtpmapid_extmap_id, sdes_item, sizeof(sdes_item)) == 0) {
- 				JANUS_LOG(LOG_DBG, "%"SCNu32"/%"SCNu16"/%"SCNu32": RTP stream ID extension: %s\n", ssrc, seq_number, timestamp, sdes_item);
++				JANUS_LOG(LOG_DBG, "%"SCNu32"/%"SCNu16"/%"SCNu32"/%d: RTP stream ID extension: %s\n",
++					ssrc, seq_number, timestamp, header->padding, sdes_item);
 +			}
 +		}
 +		if(video && session->video_active && session->ssrc[0] != 0) {
 +			/* Handle simulcast: don't relay if it's not the SSRC we wanted to handle */
 +			rtp_header *header = (rtp_header *)buf;
 +			uint32_t seq_number = ntohs(header->seq_number);
 +			uint32_t timestamp = ntohl(header->timestamp);
 +			uint32_t ssrc = ntohl(header->ssrc);
  			/* Save the frame if we're recording */
 -			janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len);
 +			if(ssrc == session->ssrc[0])
 +				janus_recorder_save_frame(session->vrc[0], buf, len);
 +			else if(ssrc == session->ssrc[1])
 +				janus_recorder_save_frame(session->vrc[1], buf, len);
 +			else if(ssrc == session->ssrc[2])
 +				janus_recorder_save_frame(session->vrc[2], buf, len);
 +			/* Access the packet payload */
 +			int plen = 0;
 +			char *payload = janus_rtp_payload(buf, len, &plen);
 +			if(payload == NULL)
 +				return;
 +			gboolean switched = FALSE;
 +			if(session->simulcast != session->simulcast_target) {
 +				/* There has been a change: let's wait for a keyframe on the target */
 +				if(ssrc == session->ssrc[session->simulcast_target]) {
 +					if(janus_vp8_is_keyframe(payload, plen)) {
- 						JANUS_LOG(LOG_WARN, "Received keyframe on SSRC %"SCNu32", switching (was %"SCNu32")\n",
- 							ssrc, session->ssrc[session->simulcast]);
++						uint32_t ssrc_old = 0;
++						if(session->simulcast != -1)
++							ssrc_old = session->ssrc[session->simulcast];
++						JANUS_LOG(LOG_WARN, "Received keyframe on SSRC %"SCNu32", switching (was %"SCNu32")\n", ssrc, ssrc_old);
 +						session->simulcast = session->simulcast_target;
 +						switched = TRUE;
 +						/* Notify the user */
 +						json_t *event = json_object();
 +						json_object_set_new(event, "echotest", json_string("event"));
 +						json_object_set_new(event, "simulcast", json_integer(session->simulcast));
 +						gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
 +						json_decref(event);
 +					} else {
 +						JANUS_LOG(LOG_WARN, "Not a keyframe on SSRC %"SCNu32" yet, waiting before switching\n", ssrc);
 +					}
 +				}
 +			}
 +			if(ssrc != session->ssrc[session->simulcast]) {
 +				JANUS_LOG(LOG_HUGE, "Dropping packet (it's from SSRC %"SCNu32", but we're only relaying SSRC %"SCNu32" now\n",
 +					ssrc, session->ssrc[session->simulcast]);
 +				return;
 +			}
 +			janus_rtp_header_update(header, &session->context, TRUE, 4500);
 +			janus_vp8_simulcast_descriptor_update(payload, plen, &session->simulcast_context, switched);
  			/* Send the frame back */
  			gateway->relay_rtp(handle, video, buf, len);
 +			/* Restore header or core statistics will be messed up */
 +			header->timestamp = htonl(timestamp);
 +			header->seq_number = htons(seq_number);
 +		} else {
 +			if((!video && session->audio_active) || (video && session->video_active)) {
 +				/* Save the frame if we're recording */
 +				janus_recorder_save_frame(video ? session->vrc[0] : session->arc, buf, len);
 +				/* Send the frame back */
 +				gateway->relay_rtp(handle, video, buf, len);
 +			}
  		}
  	}
  }
@@@ -664,9 -580,9 +670,14 @@@ void janus_echotest_slow_link(janus_plu
  			JANUS_LOG(LOG_WARN, "Getting a lot of NACKs (slow %s) for %s, forcing a lower REMB: %"SCNu64"\n",
  				uplink ? "uplink" : "downlink", video ? "video" : "audio", session->bitrate);
  			/* ... and send a new REMB back */
--			char rtcpbuf[24];
--			janus_rtcp_remb((char *)(&rtcpbuf), 24, session->bitrate);
--			gateway->relay_rtcp(handle, 1, rtcpbuf, 24);
++			char rtcpbuf[32];
++			int numssrc = 1;
++			if(session->ssrc[1])
++				numssrc++;
++			if(session->ssrc[2])
++				numssrc++;
++			int remblen = janus_rtcp_remb_ssrcs((char *)(&rtcpbuf), sizeof(rtcpbuf), session->bitrate, numssrc);
++			gateway->relay_rtcp(handle, 1, rtcpbuf, remblen);
  			/* As a last thing, notify the user about this */
  			json_t *event = json_object();
  			json_object_set_new(event, "echotest", json_string("event"));
@@@ -870,11 -754,11 +881,15 @@@ static void *janus_echotest_handler(voi
  			JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu64"\n", session->bitrate);
  			if(session->bitrate > 0) {
  				/* FIXME Generate a new REMB (especially useful for Firefox, which doesn't send any we can cap later) */
--				char buf[24];
--				memset(buf, 0, 24);
--				janus_rtcp_remb((char *)&buf, 24, session->bitrate);
++				char rtcpbuf[32];
++				int numssrc = 1;
++				if(session->ssrc[1])
++					numssrc++;
++				if(session->ssrc[2])
++					numssrc++;
++				int remblen = janus_rtcp_remb_ssrcs((char *)(&rtcpbuf), sizeof(rtcpbuf), session->bitrate, numssrc);
  				JANUS_LOG(LOG_VERB, "Sending REMB\n");
--				gateway->relay_rtcp(session->handle, 1, buf, 24);
++				gateway->relay_rtcp(session->handle, 1, rtcpbuf, remblen);
  				/* FIXME How should we handle a subsequent "no limit" bitrate? */
  			}
  		}
@@@ -967,21 -828,6 +982,25 @@@
  							/* FIXME We should notify the fact the recorder could not be created */
  							JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this EchoTest user!\n");
  						}
 +						if(session->ssrc[0] != 0) {
 +							/* Create recordings for the other layers as well */
- 							g_snprintf(filename, 255, "%s-video-sim1", recording_base);
- 							session->vrc[1] = janus_recorder_create(NULL, "vp8", filename);
- 							if(session->vrc[1] == NULL) {
- 								/* FIXME We should notify the fact the recorder could not be created */
- 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #1) for this EchoTest user!\n");
++							if(session->ssrc[1] != 0) {
++								g_snprintf(filename, 255, "%s-video-sim1", recording_base);
++								session->vrc[1] = janus_recorder_create(NULL, "vp8", filename);
++								if(session->vrc[1] == NULL) {
++									/* FIXME We should notify the fact the recorder could not be created */
++									JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #1) for this EchoTest user!\n");
++								}
 +							}
- 							g_snprintf(filename, 255, "%s-video-sim2", recording_base);
- 							session->vrc[2] = janus_recorder_create(NULL, "vp8", filename);
- 							if(session->vrc[2] == NULL) {
- 								/* FIXME We should notify the fact the recorder could not be created */
- 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #2) for this EchoTest user!\n");
++							if(session->ssrc[2] != 0) {
++								g_snprintf(filename, 255, "%s-video-sim2", recording_base);
++								session->vrc[2] = janus_recorder_create(NULL, "vp8", filename);
++								if(session->vrc[2] == NULL) {
++									/* FIXME We should notify the fact the recorder could not be created */
++									JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #2) for this EchoTest user!\n");
++								}
 +							}
 +						}
  					} else {
  						/* Build a filename */
  						g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video", session, now);
@@@ -990,21 -836,6 +1009,25 @@@
  							/* FIXME We should notify the fact the recorder could not be created */
  							JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this EchoTest user!\n");
  						}
 +						if(session->ssrc[0] != 0) {
 +							/* Create recordings for the other layers as well */
- 							g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video-sim1", session, now);
- 							session->vrc[1] = janus_recorder_create(NULL, "vp8", filename);
- 							if(session->vrc[1] == NULL) {
- 								/* FIXME We should notify the fact the recorder could not be created */
- 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #1) for this EchoTest user!\n");
++							if(session->ssrc[1] != 0) {
++								g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video-sim1", session, now);
++								session->vrc[1] = janus_recorder_create(NULL, "vp8", filename);
++								if(session->vrc[1] == NULL) {
++									/* FIXME We should notify the fact the recorder could not be created */
++									JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #1) for this EchoTest user!\n");
++								}
 +							}
- 							g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video-sim2", session, now);
- 							session->vrc[2] = janus_recorder_create(NULL, "vp8", filename);
- 							if(session->vrc[2] == NULL) {
- 								/* FIXME We should notify the fact the recorder could not be created */
- 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #2) for this EchoTest user!\n");
++							if(session->ssrc[2] != 0) {
++								g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video-sim2", session, now);
++								session->vrc[2] = janus_recorder_create(NULL, "vp8", filename);
++								if(session->vrc[2] == NULL) {
++									/* FIXME We should notify the fact the recorder could not be created */
++									JANUS_LOG(LOG_ERR, "Couldn't open an video recording file (simulcasting #2) for this EchoTest user!\n");
++								}
 +							}
 +						}
  					}
  					/* Send a PLI */
  					JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n");
diff --cc rtcp.c
index 5dd445a,5dd445a..5236d11
--- a/rtcp.c
+++ b/rtcp.c
@@@ -335,8 -335,8 +335,13 @@@ int janus_rtcp_fix_ssrc(rtcp_context *c
  						brMantissa += (_ptrRTCPData[2] << 8);
  						brMantissa += (_ptrRTCPData[3]);
  						uint64_t bitRate = brMantissa << brExp;
--						JANUS_LOG(LOG_HUGE, "       -- -- -- REMB: %u * 2^%u = %"SCNu64" (%d SSRCs, %u)\n",
--							brMantissa, brExp, bitRate, numssrc, ntohl(remb->ssrc[0]));
++						if(numssrc == 1) {
++							JANUS_LOG(LOG_HUGE, "       -- -- -- REMB: %u * 2^%u = %"SCNu64" (%d SSRCs, %u)\n",
++								brMantissa, brExp, bitRate, numssrc, ntohl(remb->ssrc[0]));
++						} else {
++							JANUS_LOG(LOG_WARN, "       -- -- -- REMB: %u * 2^%u = %"SCNu64" (%d SSRCs, %u, %u, %u)\n",
++								brMantissa, brExp, bitRate, numssrc, ntohl(remb->ssrc[0]), ntohl(remb->ssrc[1]), ntohl(remb->ssrc[2]));
++						}
  					} else {
  						JANUS_LOG(LOG_HUGE, "     #%d AFB ?? -- PSFB (206)\n", pno);
  					}
@@@ -871,7 -871,7 +876,15 @@@ int janus_rtcp_sdes(char *packet, int l
  
  /* Generate a new REMB message */
  int janus_rtcp_remb(char *packet, int len, uint64_t bitrate) {
--	if(packet == NULL || len != 24)
++	/* By default we assume a single SSRC will be set */
++	return janus_rtcp_remb_ssrcs(packet, len, bitrate, 1);
++}
++
++int janus_rtcp_remb_ssrcs(char *packet, int len, uint64_t bitrate, uint8_t numssrc) {
++	if(packet == NULL || numssrc == 0)
++		return -1;
++	int min_len = 20 + numssrc*4;
++	if(len < min_len)
  		return -1;
  	memset(packet, 0, len);
  	rtcp_header *rtcp = (rtcp_header *)packet;
@@@ -879,7 -879,7 +892,7 @@@
  	rtcp->version = 2;
  	rtcp->type = RTCP_PSFB;
  	rtcp->rc = 15;
--	rtcp->length = htons((len/4)-1);
++	rtcp->length = htons((min_len/4)-1);
  	/* Now set REMB stuff */
  	rtcp_fb *rtcpfb = (rtcp_fb *)rtcp;
  	rtcp_remb *remb = (rtcp_remb *)rtcpfb->fci;
@@@ -901,12 -901,12 +914,12 @@@
  	/* FIXME From rtcp_sender.cc */
  	unsigned char *_ptrRTCPData = (unsigned char *)remb;
  	_ptrRTCPData += 4;	/* Skip unique identifier */
--	_ptrRTCPData[0] = (uint8_t)(1);	/* Just one SSRC */
++	_ptrRTCPData[0] = numssrc;
  	_ptrRTCPData[1] = (uint8_t)((newbrexp << 2) + ((newbrmantissa >> 16) & 0x03));
  	_ptrRTCPData[2] = (uint8_t)(newbrmantissa >> 8);
  	_ptrRTCPData[3] = (uint8_t)(newbrmantissa);
  	JANUS_LOG(LOG_HUGE, "[REMB] bitrate=%"SCNu64" (%d bytes)\n", bitrate, 4*(ntohs(rtcp->length)+1));
--	return 24;
++	return min_len;
  }
  
  /* Generate a new FIR message */
diff --cc rtcp.h
index 284aac7,284aac7..84ecc2a
--- a/rtcp.h
+++ b/rtcp.h
@@@ -154,8 -154,8 +154,8 @@@ typedef struct rtcp_rem
  	char id[4];
  	/*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
  	uint32_t bitrate;
--	/*! \brief SSRC feedback */
--	uint32_t ssrc[1];
++	/*! \brief SSRC feedback (we expect at max three SSRCs in there) */
++	uint32_t ssrc[3];
  } rtcp_remb;
  
  
@@@ -367,6 -367,6 +367,14 @@@ int janus_rtcp_sdes(char *packet, int l
   * @returns The message data length in bytes, if successful, -1 on errors */
  int janus_rtcp_remb(char *packet, int len, uint64_t bitrate);
  
++/*! \brief Method to generate a new RTCP REMB message to cap the reported bitrate, but for more SSRCs
++ * @param[in] packet The buffer data (MUST be at least 24 chars)
++ * @param[in] len The message data length in bytes (MUST be 24)
++ * @param[in] bitrate The bitrate to report (e.g., 128000)
++ * @param[in] numssrc The number of SSRCs to include in the request
++ * @returns The message data length in bytes, if successful, -1 on errors */
++int janus_rtcp_remb_ssrcs(char *packet, int len, uint64_t bitrate, uint8_t numssrc);
++
  /*! \brief Method to generate a new RTCP FIR message to request a key frame
   * @param[in] packet The buffer data (MUST be at least 20 chars)
   * @param[in] len The message data length in bytes (MUST be 20)
diff --cc sdp-utils.c
index f9a5325,f9a5325..30acf96
--- a/sdp-utils.c
+++ b/sdp-utils.c
@@@ -1156,8 -1156,8 +1156,6 @@@ janus_sdp *janus_sdp_generate_answer(ja
  					am->fmts = g_list_append(am->fmts, g_strdup(fmt_str));
  				fmt = fmt->next;
  			}
--			janus_sdp_attribute *aa = janus_sdp_attribute_create("sctmap", "5000 webrtc-datachannel 16");
--			am->attributes = g_list_append(am->attributes, aa);
  		}
  		temp = temp->next;
  	}

-- 
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