[Pkg-voip-commits] [janus] 12/163: Initial experiments with VP8 temporal scalability

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 3d67fa8c3f231fb1fbe7286125bee2ce8f08950c
Author: Lorenzo Miniero <lminiero at gmail.com>
Date:   Wed Jul 5 16:38:11 2017 +0200

    Initial experiments with VP8 temporal scalability
---
 html/devicetest.html     |  19 +++++--
 html/devicetest.js       |  72 +++++++++++++++++++++------
 html/echotest.html       |  19 +++++--
 html/echotest.js         |  72 +++++++++++++++++++++------
 html/videoroomtest.html  |  45 ++---------------
 plugins/janus_echotest.c | 126 +++++++++++++++++++++++++++++++++++------------
 utils.c                  |  13 +++--
 utils.h                  |   1 +
 8 files changed, 255 insertions(+), 112 deletions(-)

diff --git a/html/devicetest.html b/html/devicetest.html
index de54a53..fb8a075 100644
--- a/html/devicetest.html
+++ b/html/devicetest.html
@@ -120,11 +120,20 @@
 								<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-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 id="simulcast" class="btn-group-vertical btn-group-vertical-xs pull-right hide">
+										<div class"row">
+											<div class="btn-group btn-group-xs" style="width: 100%">
+												<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"row">
+											<div class="btn-group btn-group-xs" style="width: 100%">
+												<button id="tl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 2" style="width: 34%">TL 2</button>
+												<button id="tl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 1" style="width: 33%">TL 1</button>
+												<button id="tl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 0" style="width: 33%">TL 0</button>
+											</div>
 										</div>
 									</div>
 									<div class="btn-group btn-group-xs pull-right hide" id="output-devices">
diff --git a/html/devicetest.js b/html/devicetest.js
index f781a8b..52c372e 100644
--- a/html/devicetest.js
+++ b/html/devicetest.js
@@ -273,54 +273,95 @@ $(document).ready(function() {
 										}
 									}
 									// Is simulcast in place?
-									var simulcast = msg["simulcast"];
-									if(simulcast !== null && simulcast !== undefined) {
+									var substream = msg["substream"];
+									var temporal = msg["temporal"];
+									if((substream !== null && substream !== undefined) || (temporal !== null && temporal !== 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});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 0 }});
 												});
 											$('#sl-1').removeClass('btn-primary btn-success').addClass('btn-success')
 												.unbind('click').click(function() {
-													toastr.info("Switching simulcast video, wait for it... (normal quality)", null, {timeOut: 2000});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 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});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 2 }});
+												});
+											$('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (lowest FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													echotest.send({message: { temporal: 0 }});
+												});
+											$('#tl-1').removeClass('btn-primary btn-success').addClass('btn-success')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													echotest.send({message: { temporal: 1 }});
+												});
+											$('#tl-2').removeClass('btn-primary btn-success').addClass('btn-success')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (highest FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													echotest.send({message: { temporal: 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});
+										if(substream === 0) {
+											toastr.success("Switched simulcast substream! (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! (normal quality)", null, {timeOut: 2000});
+										} else if(substream === 1) {
+											toastr.success("Switched simulcast substream! (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});
+										} else if(substream === 2) {
+											toastr.success("Switched simulcast substream! (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');
 										}
+										if(temporal === 0) {
+											toastr.success("Capped simulcast temporal layer! (lowest FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+										} else if(temporal === 1) {
+											toastr.success("Capped simulcast temporal layer! (medium FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+										} else if(temporal === 2) {
+											toastr.success("Capped simulcast temporal layer! (highest FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+										}
 									}
 								},
 								onlocalstream: function(stream) {
@@ -477,6 +518,9 @@ $(document).ready(function() {
 									$('#sl-0').unbind('click');
 									$('#sl-1').unbind('click');
 									$('#sl-2').unbind('click');
+									$('#tl-0').unbind('click');
+									$('#tl-1').unbind('click');
+									$('#tl-2').unbind('click');
 								}
 							});
 					},
diff --git a/html/echotest.html b/html/echotest.html
index 8542315..62b5f05 100644
--- a/html/echotest.html
+++ b/html/echotest.html
@@ -110,11 +110,20 @@
 						<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>
-									<div id="simulcast" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<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 id="simulcast" class="btn-group-vertical btn-group-vertical-xs pull-right hide">
+										<div class"row">
+											<div class="btn-group btn-group-xs" style="width: 100%">
+												<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"row">
+											<div class="btn-group btn-group-xs" style="width: 100%">
+												<button id="tl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 2" style="width: 34%">TL 2</button>
+												<button id="tl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 1" style="width: 33%">TL 1</button>
+												<button id="tl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 0" style="width: 33%">TL 0</button>
+											</div>
 										</div>
 									</div>
 								</h3>
diff --git a/html/echotest.js b/html/echotest.js
index c6a2bd5..93a37de 100644
--- a/html/echotest.js
+++ b/html/echotest.js
@@ -198,54 +198,95 @@ $(document).ready(function() {
 										}
 									}
 									// Is simulcast in place?
-									var simulcast = msg["simulcast"];
-									if(simulcast !== null && simulcast !== undefined) {
+									var substream = msg["substream"];
+									var temporal = msg["temporal"];
+									if((substream !== null && substream !== undefined) || (temporal !== null && temporal !== 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});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 0 }});
 												});
 											$('#sl-1').removeClass('btn-primary btn-success').addClass('btn-success')
 												.unbind('click').click(function() {
-													toastr.info("Switching simulcast video, wait for it... (normal quality)", null, {timeOut: 2000});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 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});
+													toastr.info("Switching simulcast substream, 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 }});
+													echotest.send({message: { substream: 2 }});
+												});
+											$('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (lowest FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													echotest.send({message: { temporal: 0 }});
+												});
+											$('#tl-1').removeClass('btn-primary btn-success').addClass('btn-success')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													echotest.send({message: { temporal: 1 }});
+												});
+											$('#tl-2').removeClass('btn-primary btn-success').addClass('btn-success')
+												.unbind('click').click(function() {
+													toastr.info("Capping simulcast temporal layer, wait for it... (highest FPS)", null, {timeOut: 2000});
+													$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+													$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+													echotest.send({message: { temporal: 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});
+										if(substream === 0) {
+											toastr.success("Switched simulcast substream! (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! (normal quality)", null, {timeOut: 2000});
+										} else if(substream === 1) {
+											toastr.success("Switched simulcast substream! (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});
+										} else if(substream === 2) {
+											toastr.success("Switched simulcast substream! (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');
 										}
+										if(temporal === 0) {
+											toastr.success("Capped simulcast temporal layer! (lowest FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+										} else if(temporal === 1) {
+											toastr.success("Capped simulcast temporal layer! (medium FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+										} else if(temporal === 2) {
+											toastr.success("Capped simulcast temporal layer! (highest FPS)", null, {timeOut: 2000});
+											$('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+											$('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+											$('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-primary');
+										}
 									}
 								},
 								onlocalstream: function(stream) {
@@ -391,6 +432,9 @@ $(document).ready(function() {
 									$('#sl-0').unbind('click');
 									$('#sl-1').unbind('click');
 									$('#sl-2').unbind('click');
+									$('#tl-0').unbind('click');
+									$('#tl-1').unbind('click');
+									$('#tl-2').unbind('click');
 								}
 							});
 					},
diff --git a/html/videoroomtest.html b/html/videoroomtest.html
index 4ec8abf..67b5d4d 100644
--- a/html/videoroomtest.html
+++ b/html/videoroomtest.html
@@ -108,14 +108,7 @@
 					<div class="col-md-4">
 						<div class="panel panel-default">
 							<div class="panel-heading">
-								<h3 class="panel-title">Remote Video #1 <span class="label label-info hide" id="remote1"></span>
-									<div id="simulcast1" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<button id="sl1-1" type="button" class="btn btn-primary" style="width: 50%">SL 1</button>
-											<button id="sl1-0" type="button" class="btn btn-primary" style="width: 50%">SL 0</button>
-										</div>
-									</div>
-								</h3>
+								<h3 class="panel-title">Remote Video #1 <span class="label label-info hide" id="remote1"></span></h3>
 							</div>
 							<div class="panel-body relative" id="videoremote1"></div>
 						</div>
@@ -123,14 +116,7 @@
 					<div class="col-md-4">
 						<div class="panel panel-default">
 							<div class="panel-heading">
-								<h3 class="panel-title">Remote Video #2 <span class="label label-info hide" id="remote2"></span>
-									<div id="simulcast2" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<button id="sl2-1" type="button" class="btn btn-primary" style="width: 50%">SL 1</button>
-											<button id="sl2-0" type="button" class="btn btn-primary" style="width: 50%">SL 0</button>
-										</div>
-									</div>
-								</h3>
+								<h3 class="panel-title">Remote Video #2 <span class="label label-info hide" id="remote2"></span></h3>
 							</div>
 							<div class="panel-body relative" id="videoremote2"></div>
 						</div>
@@ -140,14 +126,7 @@
 					<div class="col-md-4">
 						<div class="panel panel-default">
 							<div class="panel-heading">
-								<h3 class="panel-title">Remote Video #3 <span class="label label-info hide" id="remote3"></span>
-									<div id="simulcast3" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<button id="sl3-1" type="button" class="btn btn-primary" style="width: 50%">SL 1</button>
-											<button id="sl3-0" type="button" class="btn btn-primary" style="width: 50%">SL 0</button>
-										</div>
-									</div>
-								</h3>
+								<h3 class="panel-title">Remote Video #3 <span class="label label-info hide" id="remote3"></span></h3>
 							</div>
 							<div class="panel-body relative" id="videoremote3"></div>
 						</div>
@@ -155,14 +134,7 @@
 					<div class="col-md-4">
 						<div class="panel panel-default">
 							<div class="panel-heading">
-								<h3 class="panel-title">Remote Video #4 <span class="label label-info hide" id="remote4"></span>
-									<div id="simulcast4" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<button id="sl4-1" type="button" class="btn btn-primary" style="width: 50%">SL 1</button>
-											<button id="sl4-0" type="button" class="btn btn-primary" style="width: 50%">SL 0</button>
-										</div>
-									</div>
-								</h3>
+								<h3 class="panel-title">Remote Video #4 <span class="label label-info hide" id="remote4"></span></h3>
 							</div>
 							<div class="panel-body relative" id="videoremote4"></div>
 						</div>
@@ -170,14 +142,7 @@
 					<div class="col-md-4">
 						<div class="panel panel-default">
 							<div class="panel-heading">
-								<h3 class="panel-title">Remote Video #5 <span class="label label-info hide" id="remote5"></span>
-									<div id="simulcast5" class="btn-group btn-group-xs pull-right hide">
-										<div class="btn-group btn-group-xs">
-											<button id="sl5-1" type="button" class="btn btn-primary" style="width: 50%">SL 1</button>
-											<button id="sl5-0" type="button" class="btn btn-primary" style="width: 50%">SL 0</button>
-										</div>
-									</div>
-								</h3>
+								<h3 class="panel-title">Remote Video #5 <span class="label label-info hide" id="remote5"></span></h3>
 							</div>
 							<div class="panel-body relative" id="videoremote5"></div>
 						</div>
diff --git a/plugins/janus_echotest.c b/plugins/janus_echotest.c
index b6042f2..4a4ce1e 100644
--- a/plugins/janus_echotest.c
+++ b/plugins/janus_echotest.c
@@ -190,8 +190,10 @@ typedef struct janus_echotest_session {
 	uint32_t ssrc[3];		/* Only needed in case VP8 simulcasting is involved */
 	int rtpmapid_extmap_id;	/* Only needed in case Firefox's RID-based simulcasting is involved */
 	char *rid[3];			/* Only needed in case Firefox's RID-based simulcasting is involved */
-	int simulcast;			/* Which simulcast "layer" we should forward back */
-	int simulcast_target;	/* As above, but to handle transitions (e.g., wait for keyframe) */
+	int substream;			/* Which simulcast substream we should forward back */
+	int substream_target;	/* As above, but to handle transitions (e.g., wait for keyframe) */
+	int templayer;			/* Which simulcast temporal layer we should forward back */
+	int templayer_target;	/* As above, but to handle transitions (e.g., wait for keyframe) */
 	janus_vp8_simulcast_context simulcast_context;
 	janus_recorder *arc;	/* The Janus recorder instance for this user's audio, if enabled */
 	janus_recorder *vrc[3];	/* The Janus recorder instance for this user's video, if enabled (there may be more if we're simulcasting) */
@@ -399,8 +401,10 @@ void janus_echotest_create_session(janus_plugin_session *handle, int *error) {
 	session->ssrc[0] = 0;
 	session->ssrc[1] = 0;
 	session->ssrc[2] = 0;
-	session->simulcast = -1;
-	session->simulcast_target = 0;
+	session->substream = -1;
+	session->substream_target = 0;
+	session->templayer = -1;
+	session->templayer_target = 0;
 	janus_vp8_simulcast_context_reset(&session->simulcast_context);
 	session->destroyed = 0;
 	g_atomic_int_set(&session->hangingup, 0);
@@ -451,8 +455,10 @@ json_t *janus_echotest_query_session(janus_plugin_session *handle) {
 	json_object_set_new(info, "bitrate", json_integer(session->bitrate));
 	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));
+		json_object_set_new(info, "substream", json_integer(session->substream));
+		json_object_set_new(info, "substream-target", json_integer(session->substream_target));
+		json_object_set_new(info, "temporal-layer", json_integer(session->templayer));
+		json_object_set_new(info, "temporal-layer-target", json_integer(session->templayer_target));
 	}
 	if(session->arc || session->vrc[0] || session->drc) {
 		json_t *recording = json_object();
@@ -549,20 +555,20 @@ void janus_echotest_incoming_rtp(janus_plugin_session *handle, int video, char *
 			if(payload == NULL)
 				return;
 			gboolean switched = FALSE;
-			if(session->simulcast != session->simulcast_target) {
+			if(session->substream != session->substream_target) {
 				/* There has been a change: let's wait for a keyframe on the target */
-				if(ssrc == session->ssrc[session->simulcast_target]) {
+				if(ssrc == session->ssrc[session->substream_target]) {
 					if(janus_vp8_is_keyframe(payload, plen)) {
 						uint32_t ssrc_old = 0;
-						if(session->simulcast != -1)
-							ssrc_old = session->ssrc[session->simulcast];
+						if(session->substream != -1)
+							ssrc_old = session->ssrc[session->substream];
 						JANUS_LOG(LOG_WARN, "Received keyframe on SSRC %"SCNu32", switching (was %"SCNu32")\n", ssrc, ssrc_old);
-						session->simulcast = session->simulcast_target;
+						session->substream = session->substream_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));
+						json_object_set_new(event, "substream", json_integer(session->substream));
 						gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
 						json_decref(event);
 					} else {
@@ -570,11 +576,38 @@ void janus_echotest_incoming_rtp(janus_plugin_session *handle, int video, char *
 					}
 				}
 			}
-			if(ssrc != session->ssrc[session->simulcast]) {
+			if(ssrc != session->ssrc[session->substream]) {
 				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]);
+					ssrc, session->ssrc[session->substream]);
 				return;
 			}
+			/* Check if there's any temporal scalability to take into account */
+			uint16_t picid = 0;
+			uint8_t tlzi = 0;
+			uint8_t tid = 0;
+			uint8_t ybit = 0;
+			uint8_t keyidx = 0;
+			if(janus_vp8_parse_descriptor(payload, plen, &picid, &tlzi, &tid, &ybit, &keyidx) == 0) {
+				//~ JANUS_LOG(LOG_WARN, "%"SCNu16", %u, %u, %u, %u\n", picid, tlzi, tid, ybit, keyidx);
+				if(session->templayer != session->templayer_target) {
+					/* FIXME We should be smarter in deciding when to switch */
+					session->templayer = session->templayer_target;
+					/* Notify the user */
+					json_t *event = json_object();
+					json_object_set_new(event, "echotest", json_string("event"));
+					json_object_set_new(event, "temporal", json_integer(session->templayer));
+					gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
+					json_decref(event);
+				}
+				if(tid > session->templayer) {
+					JANUS_LOG(LOG_HUGE, "Dropping packet (it's temporal layer %d, but we're capping at %d)\n",
+						tid, session->templayer);
+					/* We increase the base sequence number, or there will be gaps when delivering later */
+					session->context.v_base_seq++;
+					return;
+				}
+			}
+			/* If we got here, update the RTP header and send the packet */
 			janus_rtp_header_update(header, &session->context, TRUE, 4500);
 			janus_vp8_simulcast_descriptor_update(payload, plen, &session->simulcast_context, switched);
 			/* Send the frame back */
@@ -755,8 +788,10 @@ void janus_echotest_hangup_media(janus_plugin_session *handle) {
 	session->ssrc[0] = 0;
 	session->ssrc[1] = 0;
 	session->ssrc[2] = 0;
-	session->simulcast = -1;
-	session->simulcast_target = 0;
+	session->substream = -1;
+	session->substream_target = 0;
+	session->templayer = -1;
+	session->templayer_target = 0;
 }
 
 /* Thread to handle incoming messages */
@@ -815,7 +850,8 @@ static void *janus_echotest_handler(void *data) {
 			session->ssrc[0] = json_integer_value(json_object_get(msg_simulcast, "ssrc-0"));
 			session->ssrc[1] = json_integer_value(json_object_get(msg_simulcast, "ssrc-1"));
 			session->ssrc[2] = json_integer_value(json_object_get(msg_simulcast, "ssrc-2"));
-			session->simulcast_target = 0;	/* Let's start with low quality */
+			session->substream_target = 0;	/* Let's start with low quality */
+			session->templayer_target = 2;	/* Let's start with all temporal layers */
 		}
 		json_t *audio = json_object_get(root, "audio");
 		if(audio && !json_is_boolean(audio)) {
@@ -838,11 +874,18 @@ static void *janus_echotest_handler(void *data) {
 			g_snprintf(error_cause, 512, "Invalid value (bitrate should be a positive integer)");
 			goto error;
 		}
-		json_t *simulcast = json_object_get(root, "simulcast");
-		if(simulcast && (!json_is_integer(simulcast) || (json_integer_value(simulcast) < 0 && json_integer_value(simulcast) > 2))) {
-			JANUS_LOG(LOG_ERR, "Invalid element (simulcast should be 0, 1 or 2)\n");
+		json_t *substream = json_object_get(root, "substream");
+		if(substream && (!json_is_integer(substream) || (json_integer_value(substream) < 0 && json_integer_value(substream) > 2))) {
+			JANUS_LOG(LOG_ERR, "Invalid element (substream should be 0, 1 or 2)\n");
 			error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT;
-			g_snprintf(error_cause, 512, "Invalid value (simulcast should be 0, 1 or 2)");
+			g_snprintf(error_cause, 512, "Invalid value (substream should be 0, 1 or 2)");
+			goto error;
+		}
+		json_t *temporal = json_object_get(root, "temporal");
+		if(temporal && (!json_is_integer(temporal) || (json_integer_value(temporal) < 0 && json_integer_value(temporal) > 2))) {
+			JANUS_LOG(LOG_ERR, "Invalid element (temporal should be 0, 1 or 2)\n");
+			error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT;
+			g_snprintf(error_cause, 512, "Invalid value (temporal should be 0, 1 or 2)");
 			goto error;
 		}
 		json_t *record = json_object_get(root, "record");
@@ -893,20 +936,40 @@ static void *janus_echotest_handler(void *data) {
 				/* FIXME How should we handle a subsequent "no limit" bitrate? */
 			}
 		}
-		if(simulcast) {
-			session->simulcast_target = json_integer_value(simulcast);
+		if(substream) {
+			session->substream_target = json_integer_value(substream);
 			JANUS_LOG(LOG_VERB, "Setting video SSRC to let through (simulcast): %"SCNu32" (index %d, was %d)\n",
-				session->ssrc[session->simulcast], session->simulcast_target, session->simulcast);
-			if(session->simulcast_target == session->simulcast) {
+				session->ssrc[session->substream], session->substream_target, session->substream);
+			if(session->substream_target == session->substream) {
 				/* No need to do anything, we're already getting the right substream, so 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));
+				json_object_set_new(event, "substream", json_integer(session->substream));
 				gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
 				json_decref(event);
 			} else {
 				/* We need to change substream, send a PLI */
-				JANUS_LOG(LOG_VERB, "Simulcasting change, sending a PLI to kickstart it\n");
+				JANUS_LOG(LOG_VERB, "Simulcasting substream change, sending a PLI to kickstart it\n");
+				char buf[12];
+				memset(buf, 0, 12);
+				janus_rtcp_pli((char *)&buf, 12);
+				gateway->relay_rtcp(session->handle, 1, buf, 12);
+			}
+		}
+		if(temporal) {
+			session->templayer_target = json_integer_value(temporal);
+			JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
+				session->templayer_target, session->templayer);
+			if(session->templayer_target == session->templayer) {
+				/* No need to do anything, we're already getting the right temporal, so notify the user */
+				json_t *event = json_object();
+				json_object_set_new(event, "echotest", json_string("event"));
+				json_object_set_new(event, "temporal", json_integer(session->templayer));
+				gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
+				json_decref(event);
+			} else {
+				/* We need to change temporal, send a PLI */
+				JANUS_LOG(LOG_VERB, "Simulcasting temporal layer change, sending a PLI to kickstart it\n");
 				char buf[12];
 				memset(buf, 0, 12);
 				janus_rtcp_pli((char *)&buf, 12);
@@ -1076,10 +1139,10 @@ static void *janus_echotest_handler(void *data) {
 			session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
 		}
 
-		if(!audio && !video && !bitrate && !simulcast && !record && !msg_sdp) {
-			JANUS_LOG(LOG_ERR, "No supported attributes (audio, video, bitrate, simulcast, record, jsep) found\n");
+		if(!audio && !video && !bitrate && !substream && !temporal && !record && !msg_sdp) {
+			JANUS_LOG(LOG_ERR, "No supported attributes (audio, video, bitrate, substream, temporal, record, jsep) found\n");
 			error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT;
-			g_snprintf(error_cause, 512, "Message error: no supported attributes (audio, video, bitrate, simulcast, record, jsep) found");
+			g_snprintf(error_cause, 512, "Message error: no supported attributes (audio, video, bitrate, simulcast, temporal, record, jsep) found");
 			goto error;
 		}
 
@@ -1176,7 +1239,8 @@ static void *janus_echotest_handler(void *data) {
 				json_object_set_new(simulcast, "ssrc-0", json_integer(session->ssrc[0]));
 				json_object_set_new(simulcast, "ssrc-1", json_integer(session->ssrc[1]));
 				json_object_set_new(simulcast, "ssrc-2", json_integer(session->ssrc[2]));
-				json_object_set_new(simulcast, "simulcast", json_integer(session->simulcast));
+				json_object_set_new(simulcast, "substream", json_integer(session->substream));
+				json_object_set_new(simulcast, "temporal-layer", json_integer(session->templayer));
 				json_object_set_new(info, "simulcast", simulcast);
 			}
 			if(session->arc || session->vrc[0]) {
diff --git a/utils.c b/utils.c
index f3500c2..705dde5 100644
--- a/utils.c
+++ b/utils.c
@@ -726,7 +726,7 @@ int janus_vp8_parse_descriptor(char *buffer, int len,
 	return 0;
 }
 
-static int janus_vp8_replace_descriptor(char *buffer, int len, uint16_t picid) {
+static int janus_vp8_replace_descriptor(char *buffer, int len, uint16_t picid, uint8_t tl0picidx) {
 	if(!buffer || len < 0)
 		return -1;
 	uint8_t vp8pd = *buffer;
@@ -754,8 +754,9 @@ static int janus_vp8_replace_descriptor(char *buffer, int len, uint16_t picid) {
 			}
 		}
 		if(lbit) {
-			/* FIXME Should we overwrite the TL0PICIDX octet? */
+			/* Overwrite the TL0PICIDX octet */
 			buffer++;
+			*buffer = tl0picidx;
 		}
 		if(tbit || kbit) {
 			/* Should we overwrite the TID/Y/KEYIDX octet? */
@@ -772,6 +773,9 @@ void janus_vp8_simulcast_context_reset(janus_vp8_simulcast_context *context) {
 	context->last_picid = 0;
 	context->base_picid = 0;
 	context->base_picid_prev = 0;
+	context->last_tlzi = 0;
+	context->base_tlzi = 0;
+	context->base_tlzi_prev = 0;
 }
 
 void janus_vp8_simulcast_descriptor_update(char *buffer, int len, janus_vp8_simulcast_context *context, gboolean switched) {
@@ -788,8 +792,11 @@ void janus_vp8_simulcast_descriptor_update(char *buffer, int len, janus_vp8_simu
 	if(switched) {
 		context->base_picid_prev = context->last_picid;
 		context->base_picid = picid;
+		context->base_tlzi_prev = context->last_tlzi;
+		context->base_tlzi = tlzi;
 	}
 	context->last_picid = (picid-context->base_picid)+context->base_picid_prev+1;
+	context->last_tlzi = (tlzi-context->base_tlzi)+context->base_tlzi_prev+1;
 	/* Overwrite the values in the VP8 payload descriptors with the ones we have */
-	janus_vp8_replace_descriptor(buffer, len, context->last_picid);
+	janus_vp8_replace_descriptor(buffer, len, context->last_picid, context->last_tlzi);
 }
diff --git a/utils.h b/utils.h
index 388374f..d19e764 100644
--- a/utils.h
+++ b/utils.h
@@ -241,6 +241,7 @@ gboolean janus_h264_is_keyframe(char* buffer, int len);
 /*! \brief VP8 simulcasting context, in order to make sure SSRC changes result in coherent picid/temporal level increases */
 typedef struct janus_vp8_simulcast_context {
 	uint16_t last_picid, base_picid, base_picid_prev;
+	uint8_t last_tlzi, base_tlzi, base_tlzi_prev;
 } janus_vp8_simulcast_context;
 
 /*! \brief Set (or reset) the context fields to their default values

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