pc_test_multi.html (276 lines of code) (raw):
<html><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>Simple RTCPeerConnection Video Test</title>
<style>
#player1 {
position:relative;
width:33%;
height:240px;
float:right;
}
#player2 {
position:relative;
width:66%;
height:240px;
float:left;
}
#mainvideo {
position:absolute;
top:0;
left:0;
z-index:0;
}
#player2screen {
position:absolute;
top:0;
right:0;
z-index:0;
}
#localvideo1, #localvideo2 {
position:absolute;
top:0;
left:0;
z-index:1;
}
.hidden
{
display: none;
}
</style>
</head>
<body>
<h1>Multistream & Renegotiation Test</h1>
<div>
<button id="callbutton" onclick="start();">Start Call</button>
<br>
<div>
<div id="player1">
<video tabindex="0" id="pc1video" class="mainvideo" controls="controls" height="240" width="320" muted autoplay></video>
<video tabindex="0" id="localvideo1" controls="controls" height="120" width="160" muted></video>
</div>
<div id="player2">
<div id="player2video">
<video tabindex="0" id="pc2video" class="mainvideo" controls="controls" height="240" width="320" muted autoplay></video>
<video tabindex="0" id="localvideo2" controls="controls" height="120" width="160" muted></video>
</div>
<div id="player2screen">
<div id="divscreen" class="hidden">
<button id="screenbutton" onclick="screenShare();">Share Screen</button>
</div>
<video tabindex="0" id="screenvideo" class="hidden" controls="controls" height="240" width="320" muted autoplay></video>
</div>
</div><br>
<br style="clear: left;" />
</div>
<div id="log"></div>
<script type="application/javascript">
function log(msg) {
let div = document.getElementById("log");
//div.innerHTML = div.innerHTML + "<p>" + msg + "</p>";
div.innerHTML = "<p>" + msg + "</p>" + div.innerHTML;
}
function clearlog() {
document.getElementById("log").innerHTML = "<p></p>";
}
// replace CR NL with HTML breaks
function sdpPrettyPrint(sdp) {
return sdp.replace(/[\r\n]+/g, '<br>');
}
function failed(err, constraints) {
log("Error name: " + err.name);
log("Error message: " + err.message);
log("Constraints: " + JSON.stringify(constraints));
}
let pc1video = document.getElementById("pc1video");
let pc2video = document.getElementById("pc2video");
let pc3video = document.getElementById("screenvideo");
pc1video.onplay = function() {log("Play for pc1");};
pc2video.onplay = function() {log("Play for pc2");};
pc3video.onplay = function() {log("Play for pc3");};
let localvideo1 = document.getElementById("localvideo1");
let localvideo2 = document.getElementById("localvideo2");
let callbutton = document.getElementById("callbutton");
let screenbutton = document.getElementById("screenbutton");
let pc1;
let pc2;
let pc1_offer;
let pc2_answer;
let screenStream;
let screenSenders = [];
// pc1.createOffer finished, call pc1.setLocal
function step1(offer) {
log("Offer: " + sdpPrettyPrint(offer.sdp));
pc1_offer = offer;
pc1.setLocalDescription(offer).then(step2, failed);
}
// pc1.setLocal finished, call pc2.setRemote
function step2() {
pc1.onicecandidate = function(obj) {
if (obj.candidate) {
log("pc1 found ICE candidate: " + JSON.stringify(obj.candidate));
pc2.addIceCandidate(obj.candidate);
} else {
log("pc1 got end-of-candidates signal");
}
}
pc2.setRemoteDescription(pc1_offer).then(step3, failed);
};
// pc2.setRemote finished, call pc2.createAnswer
function step3() {
pc2.didSetRemote = true;
while (pc2.ice_queued.length > 0) {
pc2.addIceCandidate(pc2.ice_queued.shift());
}
pc2.createAnswer().then(step4, failed);
}
// pc2.createAnswer finished, call pc2.setLocal
function step4(answer) {
log("Answer: " + sdpPrettyPrint(answer.sdp));
pc2_answer = answer;
pc2.setLocalDescription(answer).then(step5, failed);
}
// pc2.setLocal finished, call pc1.setRemote
function step5() {
pc2.onicecandidate = function(obj) {
if (obj.candidate) {
log("pc2 found ICE candidate: " + JSON.stringify(obj.candidate));
pc1.addIceCandidate(obj.candidate);
} else {
log("pc2 got end-of-candidates signal");
}
}
pc1.setRemoteDescription(pc2_answer).then(step6, failed);
}
// pc1.setRemote finished
function step6() {
pc1.didSetRemote = true;
while (pc1.ice_queued.length > 0) {
pc1.addIceCandidate(pc1.ice_queued.shift());
}
log("Signaling is done");
showScreenButton();
}
function showScreenButton() {
document.getElementById('divscreen').style.display = 'block';
}
function hideScreenButton() {
hideScreenVideo();
document.getElementById('divscreen').style.display = 'none';
}
function showScreenVideo() {
screenbutton.innerHTML = "Stop Screen Share";
screenbutton.onclick = stopScreenShare;
pc3video.style.display = 'block';
}
function hideScreenVideo() {
screenbutton.innerHTML = "Share Screen";
screenbutton.onclick = screenShare;
pc3video.style.display = 'none';
}
function stopScreenShare() {
screenStream.stop();
screenSenders.forEach(sender => {
pc1.removeTrack(sender);
});
screenSenders = [];
hideScreenVideo();
}
function screenShare() {
let screenConstraints = {video: {mediaSource: "screen"}};
pc1.onnegotiationneeded = function (event) {
log("pc1 onnegotiationneeded fired");
pc1.createOffer(step1, failed);
};
navigator.mediaDevices.getUserMedia(screenConstraints)
.then(stream => {
log("Got a screen share stream");
screenStream = stream;
showScreenVideo();
stream.getTracks().forEach(track => {
screenSenders.push(pc1.addTrack(track, stream));
});
});
}
function start() {
callbutton.innerHTML = "Stop Call";
callbutton.onclick = stop;
clearlog();
pc1 = new RTCPeerConnection();
pc2 = new RTCPeerConnection();
pc1.didSetRemote = false;
pc2.didSetRemote = false;
pc1.ice_queued = [];
pc2.ice_queued = [];
pc1.oniceconnectionstatechange = function() {
if (pc1.iceConnectionState == "connected") {
log("HIP HIP HORRAY PC1 is connected");
}
}
pc2.oniceconnectionstatechange = function() {
if (pc2.iceConnectionState == "connected") {
log("HIP HIP HORRAY PC2 is connected");
}
}
pc1.onicecandidate = function(obj) {
if (obj.candidate) {
log("pc1 found ICE candidate: " + JSON.stringify(obj.candidate));
if (pc2.didSetRemote) {
pc2.addIceCandidate(obj.candidate);
} else {
pc2.ice_queued.push(obj.candidate);
}
} else {
log("pc1 got end-of-candidates signal");
}
}
pc2.onicecandidate = function(obj) {
if (obj.candidate) {
log("pc2 found ICE candidate: " + JSON.stringify(obj.candidate));
if (pc1.didSetRemote) {
pc1.addIceCandidate(obj.candidate);
} else {
pc1.ice_queued.push(obj.candidate);
}
} else {
log("pc2 got end-of-candidates signal");
}
}
pc1.onaddstream = function(obj) {
log("pc1 got remote stream from pc2 " + obj.type);
pc1video.mozSrcObject = obj.stream;
}
pc2.onaddstream = function(obj) {
var stream = obj.stream;
log("pc2 remote stream has " + stream.getAudioTracks().length + " audio tracks and " + stream.getVideoTracks().length + " video tracks");
if (stream.getAudioTracks().length == 0) {
pc3video.mozSrcObject = obj.stream;
} else {
pc2video.mozSrcObject = obj.stream;
}
}
let videoConstraints = {audio: true, video: true};
let fakeVideoConstraints = {video: true, fake: true };
navigator.mediaDevices.getUserMedia(videoConstraints)
.then(stream1 => {
localvideo1.mozSrcObject = stream1;
localvideo1.play();
pc1.addStream(stream1);
navigator.mediaDevices.getUserMedia(fakeVideoConstraints)
.then(stream2 => {
localvideo2.mozSrcObject = stream2;
localvideo2.play();
pc2.addStream(stream2);
// Start the signaling.
pc1.createOffer().then(step1, failed);
});
});
}
function stop() {
pc1.close();
pc2.close();
callbutton.innerHTML = "Start Call";
callbutton.onclick = start;
hideScreenButton();
}
</script>
</body></html>