| <!DOCTYPE html> | 
 | <html> | 
 |   <!-- | 
 |   Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
 |   Use of this source code is governed by a BSD-style license that can be | 
 |   found in the LICENSE file. | 
 |   --> | 
 | <head> | 
 | <title>Video Effects Demo</title> | 
 | <style> | 
 | video { | 
 |   border:5px solid black; | 
 |   width:480px; | 
 |   height:360px; | 
 | } | 
 | button { | 
 |   font: 18px sans-serif; | 
 |   padding: 8px; | 
 | } | 
 | textarea { | 
 |   font-family: monospace; | 
 |   margin: 2px; | 
 |   width:480px; | 
 |   height:640px; | 
 | } | 
 | </style> | 
 | </head> | 
 | <body> | 
 | <table> | 
 | <tr> | 
 | <td><video id="vidlocal" autoplay></video></td> | 
 | <td><video id="vidprocessedlocal" autoplay></video></td> | 
 | <td><video id="vidremote" autoplay></video></td> | 
 | </tr> | 
 | <tr> | 
 | <td>Local Media Stream</td> | 
 | <td>Local Media Stream After Effect</td> | 
 | <td>Remote Media Stream</td> | 
 | </tr> | 
 | <tr> | 
 | </table> | 
 | <br> | 
 | <button id="startButton" onclick="start()">Start</button> | 
 | <button id="toggleEffectButton" onclick="toggleEffect()">Enable Effect</button> | 
 | <button id="callButton" onclick="call()">Call</button> | 
 | <button id="hangupButton" onclick="hangup()">Hang Up</button> | 
 | <br> | 
 | <embed id="plugin" type="application/x-ppapi-example-video-effects" | 
 |     width="320" height="240"/> | 
 |  | 
 | <script> | 
 | var RTCPeerConnection = webkitRTCPeerConnection; | 
 | var getUserMedia = navigator.webkitGetUserMedia.bind(navigator); | 
 | var attachMediaStream = function(element, stream) { | 
 |   element.src = URL.createObjectURL(stream); | 
 | }; | 
 | var startButton = document.getElementById('startButton'); | 
 | var toggleEffectButton = document.getElementById('toggleEffectButton'); | 
 | var callButton = document.getElementById('callButton'); | 
 | var hangupButton = document.getElementById('hangupButton'); | 
 |  | 
 | callButton.disabled = true; | 
 | hangupButton.disabled = true; | 
 | toggleEffectButton.disabled = true; | 
 | var pc1 = null; | 
 | var pc2 = null; | 
 | var localstream = null; | 
 | var processedLocalstream = null; | 
 | var effectsPlugin = null; | 
 | var effectsEnabled = false; | 
 |  | 
 | function trace(text) { | 
 |   // This function is used for logging. | 
 |   if (text[text.length - 1] == '\n') { | 
 |     text = text.substring(0, text.length - 1); | 
 |   } | 
 |   console.log((performance.now() / 1000).toFixed(3) + ": " + text); | 
 | } | 
 |  | 
 | function gotStream(stream){ | 
 |   trace("Received local stream"); | 
 |   // Call the polyfill wrapper to attach the media stream to this element. | 
 |   attachMediaStream(vidlocal, stream); | 
 |   localstream = stream; | 
 |   callButton.disabled = false; | 
 |   initEffect(); | 
 | } | 
 |  | 
 | function start() { | 
 |   trace("Requesting local stream"); | 
 |   startButton.disabled = true; | 
 |   // Call into getUserMedia via the polyfill (adapter.js). | 
 |   getUserMedia({audio:false, video:true}, | 
 |                 gotStream, function() {}); | 
 | }   | 
 |  | 
 | function onRegisterStreamDone() { | 
 |   vidprocessedlocal.src = URL.createObjectURL(processedLocalstream); | 
 |   toggleEffectButton.disabled = false; | 
 | } | 
 |  | 
 | function HandleMessage(message_event) { | 
 |   if (message_event.data) { | 
 |     if (message_event.data == 'DoneRegistering') { | 
 |       onRegisterStreamDone(); | 
 |     } else { | 
 |       trace(message_event.data); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | function initEffect() { | 
 |   var url = URL.createObjectURL(localstream); | 
 |   processedLocalstream = new webkitMediaStream([]); | 
 |   var processedStreamUrl = URL.createObjectURL(processedLocalstream); | 
 |   effectsPlugin.postMessage( | 
 |       'registerStream' + ' ' + url + ' ' + processedStreamUrl); | 
 | } | 
 |  | 
 | function toggleEffect() { | 
 |   effectsEnabled = !effectsEnabled; | 
 |   if (effectsEnabled) { | 
 |     toggleEffectButton.innerHTML = 'Disable Effect'; | 
 |     effectsPlugin.postMessage('effectOn'); | 
 |   } else { | 
 |     toggleEffectButton.innerHTML = 'Enable Effect'; | 
 |     effectsPlugin.postMessage('effectOff'); | 
 |   } | 
 | } | 
 |      | 
 | function call() { | 
 |   callButton.disabled = true; | 
 |   hangupButton.disabled = false; | 
 |   trace("Starting call"); | 
 |   var servers = null; | 
 |   pc1 = new RTCPeerConnection(servers); | 
 |   trace("Created local peer connection object pc1"); | 
 |   pc1.onicecandidate = iceCallback1;  | 
 |   pc2 = new RTCPeerConnection(servers); | 
 |   trace("Created remote peer connection object pc2"); | 
 |   pc2.onicecandidate = iceCallback2; | 
 |   pc2.onaddstream = gotRemoteStream;  | 
 |  | 
 |   pc1.addStream(processedLocalstream); | 
 |   trace("Adding Local Stream to peer connection"); | 
 |    | 
 |   pc1.createOffer(gotDescription1); | 
 | } | 
 |  | 
 | function gotDescription1(desc){ | 
 |   pc1.setLocalDescription(desc); | 
 |   trace("Offer from pc1 \n" + desc.sdp); | 
 |   pc2.setRemoteDescription(desc); | 
 |   // Since the "remote" side has no media stream we need | 
 |   // to pass in the right constraints in order for it to | 
 |   // accept the incoming offer of audio and video. | 
 |   var sdpConstraints = {'mandatory': { | 
 |                         'OfferToReceiveAudio':true,  | 
 |                         'OfferToReceiveVideo':true }}; | 
 |   pc2.createAnswer(gotDescription2, null, sdpConstraints); | 
 | } | 
 |  | 
 | function gotDescription2(desc){ | 
 |   pc2.setLocalDescription(desc); | 
 |   trace("Answer from pc2 \n" + desc.sdp); | 
 |   pc1.setRemoteDescription(desc); | 
 | } | 
 |  | 
 | function hangup() { | 
 |   trace("Ending call"); | 
 |   pc1.close();  | 
 |   pc2.close(); | 
 |   pc1 = null; | 
 |   pc2 = null; | 
 |   hangupButton.disabled = true; | 
 |   callButton.disabled = false; | 
 | } | 
 |  | 
 | function gotRemoteStream(e){ | 
 |   vidremote.src = URL.createObjectURL(e.stream); | 
 |   trace("Received remote stream"); | 
 | } | 
 |  | 
 | function iceCallback1(event){ | 
 |   if (event.candidate) { | 
 |     pc2.addIceCandidate(new RTCIceCandidate(event.candidate)); | 
 |     trace("Local ICE candidate: \n" + event.candidate.candidate); | 
 |   } | 
 | } | 
 |        | 
 | function iceCallback2(event){ | 
 |   if (event.candidate) { | 
 |     pc1.addIceCandidate(new RTCIceCandidate(event.candidate)); | 
 |     trace("Remote ICE candidate: \n " + event.candidate.candidate); | 
 |   } | 
 | } | 
 |  | 
 | function InitializePlugin() { | 
 |   effectsPlugin = document.getElementById('plugin'); | 
 |   effectsPlugin.addEventListener('message', HandleMessage, false); | 
 | } | 
 |  | 
 | document.addEventListener('DOMContentLoaded', InitializePlugin, false); | 
 | </script> | 
 | </body> | 
 | </html> | 
 |  | 
 |  |