263 lines
7.5 KiB
HTML
263 lines
7.5 KiB
HTML
|
<html>
|
||
|
<head>
|
||
|
<title>Constraints and Statistics</title>
|
||
|
<script>
|
||
|
var mystream;
|
||
|
var pc1;
|
||
|
var pc2;
|
||
|
|
||
|
$ = function(id) {
|
||
|
return document.getElementById(id);
|
||
|
}
|
||
|
|
||
|
function log(txt) {
|
||
|
console.log(txt);
|
||
|
}
|
||
|
|
||
|
function openCamera() {
|
||
|
if (mystream) {
|
||
|
mystream.stop();
|
||
|
}
|
||
|
navigator.webkitGetUserMedia(cameraConstraints(), gotStream, function() {
|
||
|
log("GetUserMedia failed");
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function gotStream(stream) {
|
||
|
log("GetUserMedia succeeded");
|
||
|
mystream = stream;
|
||
|
$("local-video").src = webkitURL.createObjectURL(stream);
|
||
|
}
|
||
|
|
||
|
function cameraConstraints() {
|
||
|
var constraints = {};
|
||
|
constraints.audio = true;
|
||
|
constraints.video = { mandatory: {}, optional: [] };
|
||
|
if ($("minwidth").value != "0") {
|
||
|
constraints.video.mandatory.minWidth = $("minwidth").value;
|
||
|
}
|
||
|
if ($("maxwidth").value != "0") {
|
||
|
constraints.video.mandatory.maxWidth = $("maxwidth").value;
|
||
|
}
|
||
|
if ($("minheight").value != "0") {
|
||
|
constraints.video.mandatory.minHeight = $("minheight").value;
|
||
|
}
|
||
|
if ($("maxheight").value != "0") {
|
||
|
constraints.video.mandatory.maxHeight = $("maxheight").value;
|
||
|
}
|
||
|
if ($("frameRate").value != "0") {
|
||
|
constraints.video.mandatory.minFrameRate = $("frameRate").value;
|
||
|
}
|
||
|
log('Camera constraints are ' + JSON.stringify(constraints));
|
||
|
$("cameraConstraints").innerHTML = JSON.stringify(constraints, null, ' ');
|
||
|
return constraints;
|
||
|
}
|
||
|
|
||
|
function streamConstraints() {
|
||
|
var constraints = { mandatory: {}, optional: [] };
|
||
|
if ($("bandwidth").value != "0") {
|
||
|
constraints.optional[0] = { 'bandwidth' : $('bandwidth').value };
|
||
|
}
|
||
|
log('Constraints are ' + JSON.stringify(constraints));
|
||
|
$("addStreamConstraints").innerHTML = JSON.stringify(constraints, null, ' ');
|
||
|
return constraints;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
function connect() {
|
||
|
pc1 = new webkitRTCPeerConnection(null);
|
||
|
pc2 = new webkitRTCPeerConnection(null);
|
||
|
pc1.addStream(mystream, streamConstraints());
|
||
|
log('PC1 creating offer');
|
||
|
pc1.onnegotiationeeded = function() {
|
||
|
log('Negotiation needed - PC1');
|
||
|
}
|
||
|
pc2.onnegotiationeeded = function() {
|
||
|
log('Negotiation needed - PC2');
|
||
|
}
|
||
|
pc1.onicecandidate = function(e) {
|
||
|
log('Candidate PC1');
|
||
|
if (e.candidate) {
|
||
|
pc2.addIceCandidate(new RTCIceCandidate(e.candidate));
|
||
|
}
|
||
|
}
|
||
|
pc2.onicecandidate = function(e) {
|
||
|
log('Candidate PC2');
|
||
|
if (e.candidate) {
|
||
|
pc1.addIceCandidate(new RTCIceCandidate(e.candidate));
|
||
|
}
|
||
|
}
|
||
|
pc2.onaddstream = function(e) {
|
||
|
log('PC2 got stream');
|
||
|
$('remote-video').src = webkitURL.createObjectURL(e.stream);
|
||
|
log('Remote video is ' + $('remote-video').src);
|
||
|
}
|
||
|
pc1.createOffer(function(desc) {
|
||
|
log('PC1 offering');
|
||
|
pc1.setLocalDescription(desc);
|
||
|
pc2.setRemoteDescription(desc);
|
||
|
pc2.createAnswer(function(desc2) {
|
||
|
log('PC2 answering');
|
||
|
pc2.setLocalDescription(desc2);
|
||
|
pc1.setRemoteDescription(desc2);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Display statistics
|
||
|
var statCollector = setInterval(function() {
|
||
|
var display = function(str) {
|
||
|
$('bitrate').innerHTML = str;
|
||
|
}
|
||
|
|
||
|
display("No stream");
|
||
|
if (pc2 && pc2.remoteStreams[0]) {
|
||
|
if (pc2.getStats) {
|
||
|
display('No stats callback');
|
||
|
pc2.getStats(function(stats) {
|
||
|
log('Raw stats ' + stats);
|
||
|
var statsString = '';
|
||
|
var results = stats.result();
|
||
|
log('Raw results ' + results);
|
||
|
for (var i = 0; i < results.length; ++i) {
|
||
|
var res = results[i];
|
||
|
log(i + ': ' + JSON.stringify(res));
|
||
|
statsString += '<h3>Report ';
|
||
|
statsString += i;
|
||
|
statsString += '</h3>';
|
||
|
if (res.local) {
|
||
|
statsString += "<p>Local ";
|
||
|
statsString += dumpStats(res.local);
|
||
|
}
|
||
|
if (res.remote) {
|
||
|
statsString += "<p>Remote ";
|
||
|
statsString += dumpStats(res.remote);
|
||
|
}
|
||
|
}
|
||
|
$('stats').innerHTML = statsString;
|
||
|
display('No bitrate stats');
|
||
|
});
|
||
|
} else {
|
||
|
display('No stats function. Use at least Chrome 24.0.1285');
|
||
|
}
|
||
|
} else {
|
||
|
log('Not connected yet');
|
||
|
}
|
||
|
// Collect some stats from the video tags.
|
||
|
local_video = $('local-video');
|
||
|
if (local_video) {
|
||
|
$('local-video-stats').innerHTML = local_video.videoWidth +
|
||
|
'x' + local_video.videoHeight;
|
||
|
}
|
||
|
remote_video = $('remote-video');
|
||
|
if (remote_video) {
|
||
|
$('remote-video-stats').innerHTML = remote_video.videoWidth +
|
||
|
'x' + remote_video.videoHeight;
|
||
|
}
|
||
|
}, 1000);
|
||
|
|
||
|
// Dumping a stats variable as a string.
|
||
|
// might be named toString?
|
||
|
function dumpStats(obj) {
|
||
|
var statsString = 'Timestamp:';
|
||
|
statsString += obj.timestamp;
|
||
|
if (obj.names) {
|
||
|
log('Have names function');
|
||
|
names = obj.names();
|
||
|
for (var i = 0; i < names.length; ++i) {
|
||
|
statsString += '<br>';
|
||
|
statsString += names[i];
|
||
|
statsString += ':';
|
||
|
statsString += obj.stat(names[i]);
|
||
|
}
|
||
|
} else {
|
||
|
log('No names function');
|
||
|
if (obj.stat('audioOutputLevel')) {
|
||
|
statsString += "audioOutputLevel: ";
|
||
|
statsString += obj.stat('audioOutputLevel');
|
||
|
statsString += "<br>";
|
||
|
}
|
||
|
}
|
||
|
return statsString;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Utility to show the value of a field in a span called name+Display
|
||
|
function showValue(name, value) {
|
||
|
$(name + 'Display').innerHTML = value;
|
||
|
}
|
||
|
</script>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>Constraints and Statistics</h1>
|
||
|
This page is meant to give some hints on how one can use constraints and statistics in WebRTC applications.
|
||
|
<p>
|
||
|
The form to the left gives constraints you can set on the getUserMedia call.
|
||
|
When you hit "open", it will (re)open the camera with these constraints.
|
||
|
<p>
|
||
|
The left picture is the local preview. The right picture is the picture
|
||
|
after being passed through the PeerConnection (locally).
|
||
|
<p>
|
||
|
Underneath the picture you will see a running display of how many Kbits/sec
|
||
|
the video feed uses for transmission.
|
||
|
<hr>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<td align="top">
|
||
|
<h2>getUserMedia constraints</h2>
|
||
|
<table>
|
||
|
<tr><td><td>Min<td>Max
|
||
|
<tr><td>Horizontal
|
||
|
<td><input type="range" id="minwidth" min="0" max="1280" value="300"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<td><input type="range" id="maxwidth" min="0" max="1280" value="640"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<td><span id="minwidthDisplay">300</span>-<span id="maxwidthDisplay">640</span>
|
||
|
<tr><td>Vertical
|
||
|
<td><input type="range" id="minheight" min="0" max="1280" value="200"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<td><input type="range" id="maxheight" min="0" max="1280" value="480"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<td><span id="minheightDisplay">200</span>-<span id="maxheightDisplay">480</span>
|
||
|
<tr><td>
|
||
|
FrameRate
|
||
|
<td colspan=2><input type="range" id="frameRate" min="0" max="60" value="30"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<td><span id="frameRateDisplay">30</span>
|
||
|
</table>
|
||
|
<input type="submit" name="capture" value="Capture!" onclick="openCamera()">
|
||
|
</td>
|
||
|
<td align="top">
|
||
|
<h2>addStream constraints</h2>
|
||
|
Maximum bitrate
|
||
|
<input type="range" id="bandwidth" min="0" max="2000" value="1000"
|
||
|
onchange="showValue(this.id, this.value)">
|
||
|
<span id="bandwidthDisplay">1000</span>
|
||
|
<br>
|
||
|
<input type="submit" name="connect" value="Connect!" onclick="connect()">
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<video id="local-video" autoplay width=400></video>
|
||
|
</td>
|
||
|
<td>
|
||
|
<video id="remote-video" autoplay width=400></video>
|
||
|
</td>
|
||
|
<tr>
|
||
|
<td><span id="local-video-stats"></span>
|
||
|
<td><span id="remote-video-stats"></span>
|
||
|
<br>
|
||
|
<span id="bitrate">Bitrate unknown</span>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td><pre><span id="cameraConstraints"></span></pre>
|
||
|
<td><pre><span id="addStreamConstraints"></span></pre>
|
||
|
</table>
|
||
|
<h2>Statistics report display</h2>
|
||
|
<div id="stats">Stats will appear here.</div>
|
||
|
</body>
|
||
|
</html>
|