<!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 = webkitURL.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 = webkitURL.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 = webkitURL.createObjectURL(localstream); processedLocalstream = new webkitMediaStream([]); var processedStreamUrl = webkitURL.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 = webkitURL.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>