1 <!DOCTYPE html> 2 <html> 3 <!-- 4 Copyright (c) 2013 The Chromium Authors. All rights reserved. 5 Use of this source code is governed by a BSD-style license that can be 6 found in the LICENSE file. 7 --> 8 <head> 9 <title>Video Effects Demo</title> 10 <style> 11 video { 12 border:5px solid black; 13 width:480px; 14 height:360px; 15 } 16 button { 17 font: 18px sans-serif; 18 padding: 8px; 19 } 20 textarea { 21 font-family: monospace; 22 margin: 2px; 23 width:480px; 24 height:640px; 25 } 26 </style> 27 </head> 28 <body> 29 <table> 30 <tr> 31 <td><video id="vidlocal" autoplay></video></td> 32 <td><video id="vidprocessedlocal" autoplay></video></td> 33 <td><video id="vidremote" autoplay></video></td> 34 </tr> 35 <tr> 36 <td>Local Media Stream</td> 37 <td>Local Media Stream After Effect</td> 38 <td>Remote Media Stream</td> 39 </tr> 40 <tr> 41 </table> 42 <br> 43 <button id="startButton" onclick="start()">Start</button> 44 <button id="toggleEffectButton" onclick="toggleEffect()">Enable Effect</button> 45 <button id="callButton" onclick="call()">Call</button> 46 <button id="hangupButton" onclick="hangup()">Hang Up</button> 47 <br> 48 <embed id="plugin" type="application/x-ppapi-example-video-effects" 49 width="320" height="240"/> 50 51 <script> 52 var RTCPeerConnection = webkitRTCPeerConnection; 53 var getUserMedia = navigator.webkitGetUserMedia.bind(navigator); 54 var attachMediaStream = function(element, stream) { 55 element.src = URL.createObjectURL(stream); 56 }; 57 var startButton = document.getElementById('startButton'); 58 var toggleEffectButton = document.getElementById('toggleEffectButton'); 59 var callButton = document.getElementById('callButton'); 60 var hangupButton = document.getElementById('hangupButton'); 61 62 callButton.disabled = true; 63 hangupButton.disabled = true; 64 toggleEffectButton.disabled = true; 65 var pc1 = null; 66 var pc2 = null; 67 var localstream = null; 68 var processedLocalstream = null; 69 var effectsPlugin = null; 70 var effectsEnabled = false; 71 72 function trace(text) { 73 // This function is used for logging. 74 if (text[text.length - 1] == '\n') { 75 text = text.substring(0, text.length - 1); 76 } 77 console.log((performance.now() / 1000).toFixed(3) + ": " + text); 78 } 79 80 function gotStream(stream){ 81 trace("Received local stream"); 82 // Call the polyfill wrapper to attach the media stream to this element. 83 attachMediaStream(vidlocal, stream); 84 localstream = stream; 85 callButton.disabled = false; 86 initEffect(); 87 } 88 89 function start() { 90 trace("Requesting local stream"); 91 startButton.disabled = true; 92 // Call into getUserMedia via the polyfill (adapter.js). 93 getUserMedia({audio:false, video:true}, 94 gotStream, function() {}); 95 } 96 97 function onRegisterStreamDone() { 98 vidprocessedlocal.src = URL.createObjectURL(processedLocalstream); 99 toggleEffectButton.disabled = false; 100 } 101 102 function HandleMessage(message_event) { 103 if (message_event.data) { 104 if (message_event.data == 'DoneRegistering') { 105 onRegisterStreamDone(); 106 } else { 107 trace(message_event.data); 108 } 109 } 110 } 111 112 function initEffect() { 113 var url = URL.createObjectURL(localstream); 114 processedLocalstream = new webkitMediaStream([]); 115 var processedStreamUrl = URL.createObjectURL(processedLocalstream); 116 effectsPlugin.postMessage( 117 'registerStream' + ' ' + url + ' ' + processedStreamUrl); 118 } 119 120 function toggleEffect() { 121 effectsEnabled = !effectsEnabled; 122 if (effectsEnabled) { 123 toggleEffectButton.innerHTML = 'Disable Effect'; 124 effectsPlugin.postMessage('effectOn'); 125 } else { 126 toggleEffectButton.innerHTML = 'Enable Effect'; 127 effectsPlugin.postMessage('effectOff'); 128 } 129 } 130 131 function call() { 132 callButton.disabled = true; 133 hangupButton.disabled = false; 134 trace("Starting call"); 135 var servers = null; 136 pc1 = new RTCPeerConnection(servers); 137 trace("Created local peer connection object pc1"); 138 pc1.onicecandidate = iceCallback1; 139 pc2 = new RTCPeerConnection(servers); 140 trace("Created remote peer connection object pc2"); 141 pc2.onicecandidate = iceCallback2; 142 pc2.onaddstream = gotRemoteStream; 143 144 pc1.addStream(processedLocalstream); 145 trace("Adding Local Stream to peer connection"); 146 147 pc1.createOffer(gotDescription1); 148 } 149 150 function gotDescription1(desc){ 151 pc1.setLocalDescription(desc); 152 trace("Offer from pc1 \n" + desc.sdp); 153 pc2.setRemoteDescription(desc); 154 // Since the "remote" side has no media stream we need 155 // to pass in the right constraints in order for it to 156 // accept the incoming offer of audio and video. 157 var sdpConstraints = {'mandatory': { 158 'OfferToReceiveAudio':true, 159 'OfferToReceiveVideo':true }}; 160 pc2.createAnswer(gotDescription2, null, sdpConstraints); 161 } 162 163 function gotDescription2(desc){ 164 pc2.setLocalDescription(desc); 165 trace("Answer from pc2 \n" + desc.sdp); 166 pc1.setRemoteDescription(desc); 167 } 168 169 function hangup() { 170 trace("Ending call"); 171 pc1.close(); 172 pc2.close(); 173 pc1 = null; 174 pc2 = null; 175 hangupButton.disabled = true; 176 callButton.disabled = false; 177 } 178 179 function gotRemoteStream(e){ 180 vidremote.src = URL.createObjectURL(e.stream); 181 trace("Received remote stream"); 182 } 183 184 function iceCallback1(event){ 185 if (event.candidate) { 186 pc2.addIceCandidate(new RTCIceCandidate(event.candidate)); 187 trace("Local ICE candidate: \n" + event.candidate.candidate); 188 } 189 } 190 191 function iceCallback2(event){ 192 if (event.candidate) { 193 pc1.addIceCandidate(new RTCIceCandidate(event.candidate)); 194 trace("Remote ICE candidate: \n " + event.candidate.candidate); 195 } 196 } 197 198 function InitializePlugin() { 199 effectsPlugin = document.getElementById('plugin'); 200 effectsPlugin.addEventListener('message', HandleMessage, false); 201 } 202 203 document.addEventListener('DOMContentLoaded', InitializePlugin, false); 204 </script> 205 </body> 206 </html> 207 208 209