' +
+function addSimulcastButtons(mid, temporal) {
+ $('#curres'+mid).parent().append(
+ '
');
// Enable the simulcast selection buttons
- $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Switching simulcast substream, wait for it... (lower quality)", null, {timeOut: 2000});
- if(!$('#sl-2').hasClass('btn-success'))
- $('#sl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#sl-1').hasClass('btn-success'))
- $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- streaming.send({ message: { request: "configure", substream: 0 }});
+ if(!$('#m-'+mid+'-sl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-sl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ streaming.send({ message: { request: "configure", mid: mid, substream: 0 }});
});
- $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Switching simulcast substream, wait for it... (normal quality)", null, {timeOut: 2000});
- if(!$('#sl-2').hasClass('btn-success'))
- $('#sl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- if(!$('#sl-0').hasClass('btn-success'))
- $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", substream: 1 }});
+ if(!$('#m-'+mid+'-sl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ if(!$('#m-'+mid+'-sl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, substream: 1 }});
});
- $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
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');
- if(!$('#sl-1').hasClass('btn-success'))
- $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#sl-0').hasClass('btn-success'))
- $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", substream: 2 }});
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ if(!$('#m-'+mid+'-sl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-sl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, substream: 2 }});
});
if(!temporal) // No temporal layer support
return;
- $('#tl-0').parent().removeClass('hide');
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-0').parent().removeClass('hide');
+ $('#m-'+mid+'-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});
- if(!$('#tl-2').hasClass('btn-success'))
- $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#tl-1').hasClass('btn-success'))
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- streaming.send({ message: { request: "configure", temporal: 0 }});
+ if(!$('#m-'+mid+'-tl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-tl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ streaming.send({ message: { request: "configure", mid: mid, temporal: 0 }});
});
- $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Capping simulcast temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000});
- if(!$('#tl-2').hasClass('btn-success'))
- $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-info');
- if(!$('#tl-0').hasClass('btn-success'))
- $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", temporal: 1 }});
+ if(!$('#m-'+mid+'-tl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-info');
+ if(!$('#m-'+mid+'-tl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, temporal: 1 }});
});
- $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
.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');
- if(!$('#tl-1').hasClass('btn-success'))
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#tl-0').hasClass('btn-success'))
- $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", temporal: 2 }});
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ if(!$('#m-'+mid+'-tl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-tl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, temporal: 2 }});
});
}
-function updateSimulcastButtons(substream, temporal) {
+function updateSimulcastButtons(mid, substream, temporal) {
// Check the substream
if(substream === 0) {
toastr.success("Switched simulcast substream! (lower quality)", null, {timeOut: 2000});
- $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
} else if(substream === 1) {
toastr.success("Switched simulcast substream! (normal quality)", null, {timeOut: 2000});
- $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
- $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
} 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-success').addClass('btn-primary');
- $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
}
// Check the temporal layer
if(temporal === 0) {
toastr.success("Capped simulcast temporal layer! (lowest FPS)", null, {timeOut: 2000});
- $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-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-success').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary 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-success').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
}
}
// Helpers to create SVC-related UI for a new viewer
-function addSvcButtons() {
+function addSvcButtons(mid) {
if($('#svc').length > 0)
return;
- $('#curres').parent().append(
- '
' +
+ $('#curres'+mid).parent().append(
+ '
' +
'
' +
'
' +
- ' ' +
- ' ' +
+ ' ' +
+ ' ' +
'
' +
'
' +
'
' +
'
' +
- ' ' +
- ' ' +
- ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
'
' +
'
' +
'
'
);
// Enable the VP8 simulcast selection buttons
- $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Switching SVC spatial layer, wait for it... (low resolution)", null, {timeOut: 2000});
- if(!$('#sl-1').hasClass('btn-success'))
- $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- streaming.send({ message: { request: "configure", spatial_layer: 0 }});
+ if(!$('#m-'+mid+'-sl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ streaming.send({ message: { request: "configure", mid: mid, spatial_layer: 0 }});
});
- $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Switching SVC spatial layer, wait for it... (normal resolution)", null, {timeOut: 2000});
- $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- if(!$('#sl-0').hasClass('btn-success'))
- $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", spatial_layer: 1 }});
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ if(!$('#m-'+mid+'-sl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, spatial_layer: 1 }});
});
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Capping SVC temporal layer, wait for it... (lowest FPS)", null, {timeOut: 2000});
- if(!$('#tl-2').hasClass('btn-success'))
- $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#tl-1').hasClass('btn-success'))
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- streaming.send({ message: { request: "configure", temporal_layer: 0 }});
+ if(!$('#m-'+mid+'-tl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-tl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ streaming.send({ message: { request: "configure", mid: mid, temporal_layer: 0 }});
});
- $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Capping SVC temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000});
- if(!$('#tl-2').hasClass('btn-success'))
- $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-info');
- if(!$('#tl-0').hasClass('btn-success'))
- $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", temporal_layer: 1 }});
+ if(!$('#m-'+mid+'-tl-2').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-info');
+ if(!$('#m-'+mid+'-tl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, temporal_layer: 1 }});
});
- $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary')
.unbind('click').click(function() {
toastr.info("Capping SVC temporal layer, wait for it... (highest FPS)", null, {timeOut: 2000});
- $('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
- if(!$('#tl-1').hasClass('btn-success'))
- $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
- if(!$('#tl-0').hasClass('btn-success'))
- $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
- streaming.send({ message: { request: "configure", temporal_layer: 2 }});
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info');
+ if(!$('#m-'+mid+'-tl-1').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info').addClass('btn-primary');
+ if(!$('#m-'+mid+'-tl-0').hasClass('btn-success'))
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info').addClass('btn-primary');
+ streaming.send({ message: { request: "configure", mid: mid, temporal_layer: 2 }});
});
}
-function updateSvcButtons(spatial, temporal) {
+function updateSvcButtons(mid, spatial, temporal) {
// Check the spatial layer
if(spatial === 0) {
toastr.success("Switched SVC spatial layer! (lower resolution)", null, {timeOut: 2000});
- $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
} else if(spatial === 1) {
toastr.success("Switched SVC spatial layer! (normal resolution)", null, {timeOut: 2000});
- $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
- $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-sl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
}
// Check the temporal layer
if(temporal === 0) {
toastr.success("Capped SVC temporal layer! (lowest FPS)", null, {timeOut: 2000});
- $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
} else if(temporal === 1) {
toastr.success("Capped SVC temporal layer! (medium FPS)", null, {timeOut: 2000});
- $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
- $('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
} else if(temporal === 2) {
toastr.success("Capped SVC 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-success').addClass('btn-primary');
- $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success');
+ $('#m-'+mid+'-tl-1').removeClass('btn-primary btn-success').addClass('btn-primary');
+ $('#m-'+mid+'-tl-0').removeClass('btn-primary btn-success').addClass('btn-primary');
}
}
diff --git a/html/videocalltest.js b/html/videocalltest.js
index 594f9bbf07..af05a9c7ca 100644
--- a/html/videocalltest.js
+++ b/html/videocalltest.js
@@ -52,6 +52,8 @@ var janus = null;
var videocall = null;
var opaqueId = "videocalltest-"+Janus.randomString(12);
+var localTracks = {}, localVideos = 0,
+ remoteTracks = {}, remoteVideos = 0;
var bitrateTimer = null;
var spinner = null;
@@ -128,13 +130,17 @@ $(document).ready(function() {
iceState: function(state) {
Janus.log("ICE state changed to " + state);
},
- mediaState: function(medium, on) {
- Janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium);
+ mediaState: function(medium, mid, on) {
+ Janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium + " (mid=" + mid + ")");
},
webrtcState: function(on) {
Janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
$("#videoleft").parent().unblock();
},
+ slowLink: function(uplink, lost, mid) {
+ Janus.warn("Janus reports problems " + (uplink ? "sending" : "receiving") +
+ " packets on mid " + mid + " (" + lost + " lost packets)");
+ },
onmessage: function(msg, jsep) {
Janus.debug(" ::: Got a message :::", msg);
var result = msg["result"];
@@ -309,13 +315,72 @@ $(document).ready(function() {
bitrateTimer = null;
}
},
- onlocalstream: function(stream) {
- Janus.debug(" ::: Got a local stream :::", stream);
- $('#videos').removeClass('hide').show();
- if($('#myvideo').length === 0)
- $('#videoleft').append('
');
- Janus.attachMediaStream($('#myvideo').get(0), stream);
- $("#myvideo").get(0).muted = "muted";
+ onlocaltrack: function(track, on) {
+ Janus.debug("Local track " + (on ? "added" : "removed") + ":", track);
+ // We use the track ID as name of the element, but it may contain invalid characters
+ var trackId = track.id.replace(/[{}]/g, "");
+ if(!on) {
+ // Track removed, get rid of the stream and the rendering
+ var stream = localTracks[trackId];
+ if(stream) {
+ try {
+ var tracks = stream.getTracks();
+ for(var i in tracks) {
+ var mst = tracks[i];
+ if(mst !== null && mst !== undefined)
+ mst.stop();
+ }
+ } catch(e) {}
+ }
+ if(track.kind === "video") {
+ $('#myvideo' + trackId).remove();
+ localVideos--;
+ if(localVideos === 0) {
+ // No video, at least for now: show a placeholder
+ if($('#videoleft .no-video-container').length === 0) {
+ $('#videoleft').append(
+ '
' +
+ '' +
+ 'No webcam available' +
+ '
');
+ }
+ }
+ }
+ delete localTracks[trackId];
+ return;
+ }
+ // If we're here, a new track was added
+ var stream = localTracks[trackId];
+ if(stream) {
+ // We've been here already
+ return;
+ }
+ if($('#videoleft video').length === 0) {
+ $('#videos').removeClass('hide').show();
+ }
+ if(track.kind === "audio") {
+ // We ignore local audio tracks, they'd generate echo anyway
+ if(localVideos === 0) {
+ // No video, at least for now: show a placeholder
+ if($('#videoleft .no-video-container').length === 0) {
+ $('#videoleft').append(
+ '
' +
+ '' +
+ 'No webcam available' +
+ '
');
+ }
+ }
+ } else {
+ // New video track: create a stream out of it
+ localVideos++;
+ $('#videoleft .no-video-container').remove();
+ stream = new MediaStream();
+ stream.addTrack(track.clone());
+ localTracks[trackId] = stream;
+ Janus.log("Created local stream:", stream);
+ $('#videoleft').append('
');
+ Janus.attachMediaStream($('#myvideo' + trackId).get(0), stream);
+ }
if(videocall.webrtcStuff.pc.iceConnectionState !== "completed" &&
videocall.webrtcStuff.pc.iceConnectionState !== "connected") {
$("#videoleft").parent().block({
@@ -326,74 +391,98 @@ $(document).ready(function() {
color: 'white'
}
});
- // No remote video yet
- $('#videoright').append('
');
- if(spinner == null) {
- var target = document.getElementById('videoright');
- spinner = new Spinner({top:100}).spin(target);
- } else {
- spinner.spin();
- }
}
- var videoTracks = stream.getVideoTracks();
- if(!videoTracks || videoTracks.length === 0) {
- // No webcam
- $('#myvideo').hide();
- if($('#videoleft .no-video-container').length === 0) {
- $('#videoleft').append(
- '
' +
- '' +
- 'No webcam available' +
- '
');
+ },
+ onremotetrack: function(track, mid, on) {
+ Janus.debug("Remote track (mid=" + mid + ") " + (on ? "added" : "removed") + ":", track);
+ if(!on) {
+ // Track removed, get rid of the stream and the rendering
+ var stream = remoteTracks[mid];
+ if(stream) {
+ try {
+ var tracks = stream.getTracks();
+ for(var i in tracks) {
+ var mst = tracks[i];
+ if(mst)
+ mst.stop();
+ }
+ } catch(e) {}
}
- } else {
- $('#videoleft .no-video-container').remove();
- $('#myvideo').removeClass('hide').show();
+ $('#peervideo' + mid).remove();
+ if(track.kind === "video") {
+ remoteVideos--;
+ if(remoteVideos === 0) {
+ // No video, at least for now: show a placeholder
+ if($('#videoright .no-video-container').length === 0) {
+ $('#videoright').append(
+ '
' +
+ '' +
+ 'No remote video available' +
+ '
');
+ }
+ }
+ }
+ delete remoteTracks[mid];
+ return;
}
- },
- onremotestream: function(stream) {
- Janus.debug(" ::: Got a remote stream :::", stream);
+ // If we're here, a new track was added
var addButtons = false;
- if($('#remotevideo').length === 0) {
+ if($('#videoright audio').length === 0 && $('#videoright video').length === 0) {
addButtons = true;
- $('#videoright').append('
');
- // Show the video, hide the spinner and show the resolution when we get a playing event
- $("#remotevideo").bind("playing", function () {
- $('#waitingvideo').remove();
- if(this.videoWidth)
- $('#remotevideo').removeClass('hide').show();
- if(spinner)
- spinner.stop();
- spinner = null;
- var width = this.videoWidth;
- var height = this.videoHeight;
- $('#curres').removeClass('hide').text(width+'x'+height).show();
- });
- $('#callee').removeClass('hide').html(yourusername).show();
+ $('#videos').removeClass('hide').show();
}
- Janus.attachMediaStream($('#remotevideo').get(0), stream);
- var videoTracks = stream.getVideoTracks();
- if(!videoTracks || videoTracks.length === 0) {
- // No remote video
- $('#remotevideo').hide();
- if($('#videoright .no-video-container').length === 0) {
- $('#videoright').append(
- '
' +
- '' +
- 'No remote video available' +
- '
');
+ if(track.kind === "audio") {
+ // New audio track: create a stream out of it, and use a hidden