Merge branch 'gh-pages' into contributing-server
diff --git a/src/content/insertable-streams/endtoend-encryption/js/videopipe.js b/src/content/insertable-streams/endtoend-encryption/js/videopipe.js
index 2404583..b589cc8 100644
--- a/src/content/insertable-streams/endtoend-encryption/js/videopipe.js
+++ b/src/content/insertable-streams/endtoend-encryption/js/videopipe.js
@@ -43,7 +43,7 @@
     const {codecs} = RTCRtpSender.getCapabilities('video');
     const selectedCodecIndex = codecs.findIndex(c => c.mimeType === preferredVideoCodecMimeType);
     const selectedCodec = codecs[selectedCodecIndex];
-    codecs.slice(selectedCodecIndex, 1);
+    codecs.splice(selectedCodecIndex, 1);
     codecs.unshift(selectedCodec);
     const transceiver = this.pc1.getTransceivers().find(t => t.sender && t.sender.track === stream.getVideoTracks()[0]);
     transceiver.setCodecPreferences(codecs);
diff --git a/src/content/peerconnection/audio/index.html b/src/content/peerconnection/audio/index.html
index f2f91f7..d8eb6b1 100644
--- a/src/content/peerconnection/audio/index.html
+++ b/src/content/peerconnection/audio/index.html
@@ -73,6 +73,10 @@
         <div>Packets sent per second</div>
         <canvas id="packetCanvas"></canvas>
     </div>
+    <div class="graph-container" id="audioLevelGraph">
+        <div>average audio level ([0..1])</div>
+        <canvas id="audioLevelCanvas"></canvas>
+    </div>
 
     <a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/peerconnection/audio"
        title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
diff --git a/src/content/peerconnection/audio/js/main.js b/src/content/peerconnection/audio/js/main.js
index 6097796..241388f 100644
--- a/src/content/peerconnection/audio/js/main.js
+++ b/src/content/peerconnection/audio/js/main.js
@@ -36,6 +36,10 @@
   voiceActivityDetection: false
 };
 
+const audioLevels = [];
+let audioLevelGraph;
+let audioLevelSeries;
+
 // Enabling opus DTX is an expert option without GUI.
 // eslint-disable-next-line prefer-const
 let useDtx = false;
@@ -102,7 +106,7 @@
       console.log(JSON.stringify(codecs, null, ' '));
       const selectedCodecIndex = codecs.findIndex(c => c.mimeType === mimeType && c.clockRate === parseInt(clockRate, 10) && c.sdpFmtpLine === sdpFmtpLine);
       const selectedCodec = codecs[selectedCodecIndex];
-      codecs.slice(selectedCodecIndex, 1);
+      codecs.splice(selectedCodecIndex, 1);
       codecs.unshift(selectedCodec);
       const transceiver = pc1.getTransceivers().find(t => t.sender && t.sender.track === localStream.getAudioTracks()[0]);
       transceiver.setCodecPreferences(codecs);
@@ -123,6 +127,10 @@
   packetSeries = new TimelineDataSeries();
   packetGraph = new TimelineGraphView('packetGraph', 'packetCanvas');
   packetGraph.updateEndDate();
+
+  audioLevelSeries = new TimelineDataSeries();
+  audioLevelGraph = new TimelineGraphView('audioLevelGraph', 'audioLevelCanvas');
+  audioLevelGraph.updateEndDate();
 }
 
 function onCreateSessionDescriptionError(error) {
@@ -359,3 +367,33 @@
     lastResult = res;
   });
 }, 1000);
+
+if (window.RTCRtpReceiver && ('getSynchronizationSources' in window.RTCRtpReceiver.prototype)) {
+  let lastTime;
+  const getAudioLevel = (timestamp) => {
+    window.requestAnimationFrame(getAudioLevel);
+    if (!pc2) {
+      return;
+    }
+    const receiver = pc2.getReceivers().find(r => r.track.kind === 'audio');
+    if (!receiver) {
+      return;
+    }
+    const sources = receiver.getSynchronizationSources();
+    sources.forEach(source => {
+      audioLevels.push(source.audioLevel);
+    });
+    if (!lastTime) {
+      lastTime = timestamp;
+    } else if (timestamp - lastTime > 500 && audioLevels.length > 0) {
+      // Update graph every 500ms.
+      const maxAudioLevel = Math.max.apply(null, audioLevels);
+      audioLevelSeries.addPoint(Date.now(), maxAudioLevel);
+      audioLevelGraph.setDataSeries([audioLevelSeries]);
+      audioLevelGraph.updateEndDate();
+      audioLevels.length = 0;
+      lastTime = timestamp;
+    }
+  };
+  window.requestAnimationFrame(getAudioLevel);
+}
diff --git a/src/content/peerconnection/change-codecs/js/main.js b/src/content/peerconnection/change-codecs/js/main.js
index ff2debd..1e1e3fd 100644
--- a/src/content/peerconnection/change-codecs/js/main.js
+++ b/src/content/peerconnection/change-codecs/js/main.js
@@ -119,7 +119,7 @@
       const {codecs} = RTCRtpSender.getCapabilities('video');
       const selectedCodecIndex = codecs.findIndex(c => c.mimeType === mimeType && c.sdpFmtpLine === sdpFmtpLine);
       const selectedCodec = codecs[selectedCodecIndex];
-      codecs.slice(selectedCodecIndex, 1);
+      codecs.splice(selectedCodecIndex, 1);
       codecs.unshift(selectedCodec);
       console.log(codecs);
       const transceiver = pc1.getTransceivers().find(t => t.sender && t.sender.track === localStream.getVideoTracks()[0]);
diff --git a/src/content/peerconnection/constraints/js/main.js b/src/content/peerconnection/constraints/js/main.js
index 9f55c77..93ad1f6 100644
--- a/src/content/peerconnection/constraints/js/main.js
+++ b/src/content/peerconnection/constraints/js/main.js
@@ -89,7 +89,7 @@
   navigator.mediaDevices.getUserMedia(getUserMediaConstraints())
       .then(gotStream)
       .catch(e => {
-        const message = `getUserMedia error: ${e.name}\nPermissionDeniedError may mean invalid constraints.`;
+        const message = `getUserMedia error: ${e.name}\nThis may mean invalid constraints.`;
         alert(message);
         console.log(message);
         getMediaButton.disabled = false;
@@ -151,8 +151,8 @@
   remotePeerConnection = new RTCPeerConnection(null);
   localStream.getTracks().forEach(track => localPeerConnection.addTrack(track, localStream));
   console.log('localPeerConnection creating offer');
-  localPeerConnection.onnegotiationeeded = () => console.log('Negotiation needed - localPeerConnection');
-  remotePeerConnection.onnegotiationeeded = () => console.log('Negotiation needed - remotePeerConnection');
+  localPeerConnection.onnegotiationneeded = () => console.log('Negotiation needed - localPeerConnection');
+  remotePeerConnection.onnegotiationneeded = () => console.log('Negotiation needed - remotePeerConnection');
   localPeerConnection.onicecandidate = e => {
     console.log('Candidate localPeerConnection');
     remotePeerConnection
diff --git a/src/content/peerconnection/old-new-stats/js/main.js b/src/content/peerconnection/old-new-stats/js/main.js
index 4db1c08..3d525c4 100644
--- a/src/content/peerconnection/old-new-stats/js/main.js
+++ b/src/content/peerconnection/old-new-stats/js/main.js
@@ -136,10 +136,10 @@
     remotePeerConnection = new RTCPeerConnection(null);
     localStream.getTracks().forEach(track => localPeerConnection.addTrack(track, localStream));
     console.log('localPeerConnection creating offer');
-    localPeerConnection.onnegotiationeeded = () => {
+    localPeerConnection.onnegotiationneeded = () => {
       console.log('Negotiation needed - localPeerConnection');
     };
-    remotePeerConnection.onnegotiationeeded = () => {
+    remotePeerConnection.onnegotiationneeded = () => {
       console.log('Negotiation needed - remotePeerConnection');
     };
 
@@ -270,4 +270,4 @@
 
   // Start it all up
   displayGetUserMediaConstraints();
-});
\ No newline at end of file
+});