pc_test_loop.html (340 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:640px;
height:480px;
float:right;
}
#player2 {
position:relative;
width:640px;
height:480px;
float:left;
}
#mainvideo {
position:absolute;
top:0;
left:0;
z-index:0;
}
#localvideo1, #localvideo2 {
position:absolute;
top:0;
left:0;
z-index:1;
}
.hidden
{
display: none;
}
</style>
</head>
<body>
<h1>Simple RTCPeerConnection Video Test</h1>
<div>
<div id="player1">
<video tabindex="0" id="pc1video" class="mainvideo" controls="controls" height="480" width="640" muted autoplay></video>
<video tabindex="0" id="localvideo1" controls="controls" height="120" width="160" muted></video></div>
<div id="player2">
<video tabindex="0" id="pc2video" class="mainvideo" controls="controls" height="480" width="640" muted autoplay></video>
<video tabindex="0" id="localvideo2" controls="controls" height="120" width="160" muted></video></div><br>
<br style="clear: left;" />
</div>
<div id="log"></div>
<script type="application/javascript">
// Lame clone function. Not efficient but we don't have jQuery.
function copy_dictionary (obj) {
return JSON.parse(JSON.stringify(obj));
}
function log(msg) {
let div = document.getElementById("log");
div.innerHTML = "<p>" + msg + "</p>" + div.innerHTML;
}
var rounds = 0;
let pc1video = document.getElementById("pc1video");
let pc2video = document.getElementById("pc2video");
//pc1video.onplay = function() {log("Play for pc1");};
//pc2video.onplay = function() {log("Play for pc2");};
let button = document.getElementById("tehbutton");
let localvideo1 = document.getElementById("localvideo1");
let localvideo2 = document.getElementById("localvideo2");
let pc1;
let pc2;
let pc1_offer;
let pc2_answer;
let offer_constraints;
let answer_constraints;
function disable_media(pc, type, array_index) {
log("disable_media");
var stream;
if (pc == pc1)
stream = localvideo1.mozSrcObject;
else if (pc == pc2)
stream = localvideo2.mozSrcObject;
else log("bad pc " + pc);
if (stream) {
log("track[" + array_index + "] = " + stream.getVideoTracks()[array_index]);
if (type == 1)
stream.getVideoTracks()[array_index].enabled = !video_disable.checked;
else if (type == 0)
stream.getAudioTracks()[array_index].enabled = !audio_disable.checked;
else
log("bad type");
}
else
log("no stream");
}
function failed(code) {
var err = code.message || code;
console.log(err);
log("Failure callback: " + JSON.stringify(err));
}
// replace CR NL with HTML breaks
function sdpPrettyPrint(sdp) {
return sdp.replace(/[\r\n]+/g, '<br>');
}
function h264StateChange(cb)
{
var h264opts = document.getElementById('divH264');
if(cb.checked){
h264opts.style.display = 'block';
} else {
h264opts.style.display = 'none';
}
}
// Also remove mode 0 if it's offered
// Note, we don't bother removing the fmtp lines, which makes a good test
// for some SDP parsing issues.
function removeAllButH264(sdp) {
updated_sdp = sdp.replace("a=rtpmap:120 VP8/90000\r\n","");
updated_sdp = updated_sdp.replace("a=rtpmap:121 VP9/90000\r\n","");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*) 120/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF $2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF 120([0-9 ]*)/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF$2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*) 121/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF $2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF 121([0-9 ]*)/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF$2");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 goog-remb\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:121 goog-remb\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:121 nack\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:121 nack pli\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:121 ccm fir\r\n","");
return updated_sdp;
}
function removeAllButVP9(sdp) {
updated_sdp = sdp.replace("a=rtpmap:120 VP8/90000\r\n","");
updated_sdp = updated_sdp.replace("a=rtpmap:126 H264/90000\r\n","");
updated_sdp = updated_sdp.replace("a=rtpmap:97 H264/90000\r\n","");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*) 120/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF $2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF 120([0-9 ]*)/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF$2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*) 126/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF $2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF 126([0-9 ]*)/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF$2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*) 97/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF $2");
updated_sdp = updated_sdp.replace(/m=video ([0-9]+) UDP\/TLS\/RTP\/SAVPF 97([0-9 ]*)/g, "m=video $1 UDP\/TLS\/RTP\/SAVPF$2");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 goog-remb\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:126 goog-remb\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:126 nack\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:126 nack pli\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:126 ccm fir\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:97 goog-remb\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:97 nack\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:97 nack pli\r\n","");
updated_sdp = updated_sdp.replace("a=rtcp-fb:97 ccm fir\r\n","");
return updated_sdp;
}
// Remove anything that's not G.722 from the m=audio line
function removeNonG722(sdp) {
updated_sdp = sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, "m=audio $1 RTP\/SAVPF 9");
updated_sdp = updated_sdp.replace(/m=audio ([0-9]+) UDP\/TLS\/RTP\/SAVPF ([0-9 ]*)/g, "m=audio $1 UDP\/TLS\/RTP\/SAVPF 9");
return updated_sdp;
}
function preferMode0(sdp) {
updated_sdp = updated_sdp.replace("126 97", "97 126");
return updated_sdp;
}
// pc1.createOffer finished, call pc1.setLocal
function offerSetLocal(offer) {
//log("Offer: " + sdpPrettyPrint(offer.sdp));
pc1_offer = offer;
return pc1.setLocalDescription(offer);
}
// pc1.setLocal finished, call pc2.setRemote
function offerSetRemote() {
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");
}
}
return pc2.setRemoteDescription(pc1_offer);
};
// pc2.setRemote finished, call pc2.createAnswer
function createAnswer() {
pc2.didSetRemote = true;
while (pc2.ice_queued.length > 0) {
pc2.addIceCandidate(pc2.ice_queued.shift());
}
return pc2.createAnswer(answer_constraints);
}
// pc2.createAnswer finished, call pc2.setLocal
function answerSetLocal(answer) {
//log("Answer: " + sdpPrettyPrint(answer.sdp));
pc2_answer = answer;
return pc2.setLocalDescription(answer);
}
// pc2.setLocal finished, call pc1.setRemote
function answerSetRemote() {
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");
}
}
return pc1.setRemoteDescription(pc2_answer);
}
// pc1.setRemote finished, media should be running!
function finished() {
pc1.didSetRemote = true;
while (pc1.ice_queued.length > 0) {
pc1.addIceCandidate(pc1.ice_queued.shift());
}
//log("Signaling done");
}
function connected() {
if ((pc1.iceConnectionState == 'connected') &&
(pc2.iceConnectionState == 'connected')){
log("call " + rounds + " connected");
setTimeout(stop, 5000);
}
}
function start() {
var pc1config = {};
var pc2config = {};
let idp;
var myrequest = {};
myrequest.video = true;
myrequest.audio = false;
myrequest_reverse = copy_dictionary(myrequest);
myrequest_reverse.fake = true;
offer_constraints = null;
answer_constraints = null;
pc1 = new RTCPeerConnection(pc1config);
pc2 = new RTCPeerConnection(pc2config);
pc1.didSetRemote = false;
pc2.didSetRemote = false;
pc1.ice_queued = [];
pc2.ice_queued = [];
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.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");
}
}
pc1.oniceconnectionstatechange = function(obj) {
//log("pc1 ICE connection state: " + pc1.iceConnectionState);
if (pc1.iceConnectionState == 'connected') {
//log('PC1: HIP HIP HOORAY');
connected();
}
}
pc2.oniceconnectionstatechange = function(obj) {
//log("pc2 ICE connection state: " + pc2.iceConnectionState);
if (pc2.iceConnectionState == 'connected') {
//log('PC2: HIP HIP HOORAY');
connected();
}
}
pc1.ontrack = function(obj) {
//log("pc1 got remote tracks from pc2 " + obj.type);
pc1video.mozSrcObject = obj.streams[0];
}
pc2.ontrack = function(obj) {
//log("pc2 got remote tracks from pc1 " + obj.type);
pc2video.mozSrcObject = obj.streams[0];
}
if (idp) {
pc1.setIdentityProvider(idp.domain, idp.protocol, idp.usera);
pc1.peerIdentity.then(id => log('pc1 connected to "' + id.name + '"'));
pc2.setIdentityProvider(idp.domain, idp.protocol, idp.userb);
pc2.peerIdentity.then(id => log('pc2 connected to "' + id.name + '"'));
}
navigator.mediaDevices.getUserMedia(myrequest).then(function(video1) {
// Add stream obtained from gUM to <video> to start media flow.
localvideo1.mozSrcObject = video1;
localvideo1.play();
pc1.addStream(video1);
return Promise.resolve();
})
.then(() => {
return navigator.mediaDevices.getUserMedia(myrequest_reverse).then(function(video2) {
localvideo2.mozSrcObject = video2;
localvideo2.play();
pc2.addStream(video2);
return Promise.resolve();
});
})
.then(() => {
return pc1.createOffer(offer_constraints);
})
.then((offer) => {
return offerSetLocal(offer);
})
.then(() => {
return offerSetRemote();
})
.then(() => {
return createAnswer();
})
.then((answer) => {
return answerSetLocal(answer);
})
.then(() => {
return answerSetRemote();
})
.then(() => {
finished();
})
.catch((e) => {
failed(e);
});
}
function stop() {
pc1.close();
pc2.close();
rounds += 1;
if (rounds < 30) {
start();
}
}
window.onload = start();
</script>
</body></html>