Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spatial audio support in AudioBridge via stereo mixing #2446

Merged
merged 10 commits into from
May 27, 2021
1 change: 1 addition & 0 deletions conf/janus.plugin.audiobridge.jcfg.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# secret = "<optional password needed for manipulating (e.g. destroying) the room>"
# pin = "<optional password needed for joining the room>"
# sampling_rate = <sampling rate> (e.g., 16000 for wideband mixing)
# spatial_audio = true|false (if true, the mix will be stereo to spatially place users, default=false)
# audiolevel_ext = true|false (whether the ssrc-audio-level RTP extension must
# be negotiated/used or not for new joins, default=true)
# audiolevel_event = true|false (whether to emit event to other users or not, default=false)
Expand Down
5 changes: 4 additions & 1 deletion html/audiobridgetest.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.6.2/bootstrap-slider.min.js"></script>
<script type="text/javascript" src="janus.js" ></script>
<script type="text/javascript" src="audiobridgetest.js"></script>
<script>
Expand All @@ -25,6 +26,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.4.0/cerulean/bootstrap.min.css" type="text/css"/>
<link rel="stylesheet" href="css/demo.css" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.6.2/css/bootstrap-slider.css" type="text/css"/>
</head>
<body>

Expand Down Expand Up @@ -80,7 +82,8 @@ <h3>Demo details</h3>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Participants <span class="label label-info hide" id="participant"></span>
<button class="btn-xs btn-danger hide pull-right" autocomplete="off" id="toggleaudio">Mute</button></h3>
<button class="btn-xs btn-danger hide pull-right" autocomplete="off" id="toggleaudio">Mute</button>
<button class="btn-xs btn-primary hide pull-right" autocomplete="off" id="position">Position</button></h3>
</div>
<div class="panel-body">
<ul id="list" class="list-group">
Expand Down
102 changes: 79 additions & 23 deletions html/audiobridgetest.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ var spinner = null;
var myroom = 1234; // Demo room
if(getQueryStringValue("room") !== "")
myroom = parseInt(getQueryStringValue("room"));
var stereo = false;
if(getQueryStringValue("stereo") !== "")
stereo = (getQueryStringValue("stereo") === "true");
var myusername = null;
var myid = null;
var webrtcUp = false;
Expand Down Expand Up @@ -146,7 +149,13 @@ $(document).ready(function() {
// Publish our stream
mixertest.createOffer(
{
media: { video: false}, // This is an audio only room
media: { video: false }, // This is an audio only room
customizeSdp: function(jsep) {
if(stereo && jsep.sdp.indexOf("stereo=1") == -1) {
// Make sure that our offer contains stereo too
jsep.sdp = jsep.sdp.replace("useinbandfec=1", "useinbandfec=1;stereo=1");
}
},
success: function(jsep) {
Janus.debug("Got SDP!", jsep);
var publish = { request: "configure", muted: false };
Expand All @@ -168,22 +177,34 @@ $(document).ready(function() {
var display = list[f]["display"];
var setup = list[f]["setup"];
var muted = list[f]["muted"];
var spatial = list[f]["spatial_position"];
Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")");
if($('#rp'+id).length === 0) {
if($('#rp' + id).length === 0) {
// Add to the participants list
$('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+
var slider = '';
if(spatial !== null && spatial !== undefined)
slider = '<span>[L <input id="sp' + id + '" type="text" style="width: 10%;"/> R] </span>';
$('#list').append('<li id="rp' + id +'" class="list-group-item">' +
slider +
display +
' <i class="absetup fa fa-chain-broken"></i>' +
' <i class="abmuted fa fa-microphone-slash"></i></li>');
$('#rp'+id + ' > i').hide();
if(spatial !== null && spatial !== undefined) {
$('#sp' + id).slider({ min: 0, max: 100, step: 1, value: 50, handle: 'triangle', enabled: false });
$('#position').removeClass('hide').show();
}
$('#rp' + id + ' > i').hide();
}
if(muted === true || muted === "true")
$('#rp'+id + ' > i.abmuted').removeClass('hide').show();
$('#rp' + id + ' > i.abmuted').removeClass('hide').show();
else
$('#rp'+id + ' > i.abmuted').hide();
$('#rp' + id + ' > i.abmuted').hide();
if(setup === true || setup === "true")
$('#rp'+id + ' > i.absetup').hide();
$('#rp' + id + ' > i.absetup').hide();
else
$('#rp'+id + ' > i.absetup').removeClass('hide').show();
$('#rp' + id + ' > i.absetup').removeClass('hide').show();
if(spatial !== null && spatial !== undefined)
$('#sp' + id).slider('setValue', spatial);
}
}
} else if(event === "roomchanged") {
Expand All @@ -200,22 +221,34 @@ $(document).ready(function() {
var display = list[f]["display"];
var setup = list[f]["setup"];
var muted = list[f]["muted"];
var spatial = list[f]["spatial_position"];
Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")");
if($('#rp'+id).length === 0) {
if($('#rp' + id).length === 0) {
// Add to the participants list
$('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+
var slider = '';
if(spatial !== null && spatial !== undefined)
slider = '<span>[L <input id="sp' + id + '" type="text" style="width: 10%;"/> R] </span>';
$('#list').append('<li id="rp' + id +'" class="list-group-item">' +
slider +
display +
' <i class="absetup fa fa-chain-broken"></i>' +
' <i class="abmuted fa fa-microphone-slash"></i></li>');
$('#rp'+id + ' > i').hide();
if(spatial !== null && spatial !== undefined) {
$('#sp' + id).slider({ min: 0, max: 100, step: 1, value: 50, handle: 'triangle', enabled: false });
$('#position').removeClass('hide').show();
}
$('#rp' + id + ' > i').hide();
}
if(muted === true || muted === "true")
$('#rp'+id + ' > i.abmuted').removeClass('hide').show();
$('#rp' + id + ' > i.abmuted').removeClass('hide').show();
else
$('#rp'+id + ' > i.abmuted').hide();
$('#rp' + id + ' > i.abmuted').hide();
if(setup === true || setup === "true")
$('#rp'+id + ' > i.absetup').hide();
$('#rp' + id + ' > i.absetup').hide();
else
$('#rp'+id + ' > i.absetup').removeClass('hide').show();
$('#rp' + id + ' > i.absetup').removeClass('hide').show();
if(spatial !== null && spatial !== undefined)
$('#sp' + id).slider('setValue', spatial);
}
}
} else if(event === "destroyed") {
Expand All @@ -233,22 +266,34 @@ $(document).ready(function() {
var display = list[f]["display"];
var setup = list[f]["setup"];
var muted = list[f]["muted"];
var spatial = list[f]["spatial_position"];
Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")");
if($('#rp'+id).length === 0) {
if($('#rp' + id).length === 0) {
// Add to the participants list
$('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+
var slider = '';
if(spatial !== null && spatial !== undefined)
slider = '<span>[L <input id="sp' + id + '" type="text" style="width: 10%;"/> R] </span>';
$('#list').append('<li id="rp' + id +'" class="list-group-item">' +
slider +
display +
' <i class="absetup fa fa-chain-broken"></i>' +
' <i class="abmuted fa fa-microphone-slash"></i></li>');
$('#rp'+id + ' > i').hide();
if(spatial !== null && spatial !== undefined) {
$('#sp' + id).slider({ min: 0, max: 100, step: 1, value: 50, handle: 'triangle', enabled: false });
$('#position').removeClass('hide').show();
}
$('#rp' + id + ' > i').hide();
}
if(muted === true || muted === "true")
$('#rp'+id + ' > i.abmuted').removeClass('hide').show();
$('#rp' + id + ' > i.abmuted').removeClass('hide').show();
else
$('#rp'+id + ' > i.abmuted').hide();
$('#rp' + id + ' > i.abmuted').hide();
if(setup === true || setup === "true")
$('#rp'+id + ' > i.absetup').hide();
$('#rp' + id + ' > i.absetup').hide();
else
$('#rp'+id + ' > i.absetup').removeClass('hide').show();
$('#rp' + id + ' > i.absetup').removeClass('hide').show();
if(spatial !== null && spatial !== undefined)
$('#sp' + id).slider('setValue', spatial);
}
} else if(msg["error"]) {
if(msg["error_code"] === 485) {
Expand Down Expand Up @@ -306,7 +351,18 @@ $(document).ready(function() {
$('#toggleaudio').html("Unmute").removeClass("btn-danger").addClass("btn-success");
mixertest.send({ message: { request: "configure", muted: !audioenabled }});
}).removeClass('hide').show();

// Spatial position, if enabled
$('#position').click(
function() {
bootbox.prompt("Insert new spatial position: [0-100] (0=left, 50=center, 100=right)", function(result) {
var spatial = parseInt(result);
if(isNaN(spatial) || spatial < 0 || spatial > 100) {
bootbox.alert("Invalid value");
return;
}
mixertest.send({ message: { request: "configure", spatial_position: spatial }});
});
});
},
oncleanup: function() {
webrtcUp = false;
Expand Down
Loading