WebRTCを使ってみた
http://ando-yasushi.appspot.com/wavycamera/index.html
WebRTCを使うとJavaScriptでカメラやマイクを扱えるということで、ちょっと試してみてることがあったんだけど、いくらやってもなかなかうまい感じに動かないので気分転換に他のもの作ってみた。わりとさくっと。
どこかで見た動画が元ネタなんだけど見つからず・・・。
WebRTCはまだChrome開発版くらいでしか使えず、しかも chrome://flags でMediaStreamを有効にする必要があるから、いま作っても見られる人は少ないんだろうけど、JavaScriptでリアルタイムに映像を処理できるってやっぱり夢が広がるねぇ。
使い方
Chrome Canaryをダウンロード
ウェブ系の開発者ならChrome Canaryくらいインストール済みのはずなんだけど、まぁ一応。
http://tools.google.com/dlpage/chromesxs
で、インストール
試す
http://ando-yasushi.appspot.com/wavycamera/index.html
マシンパワーに結構依存するかも。WebWorkerとか使って重そうな処理は裏に回すべきなのかな・・・。
なにはともあれEnjoy!
作り方
リファクタリング前で恥ずかしいけども。
カメラ入力を読み取り
navigator.webkitGetUserMedia関数の第一引数に 'video'*1 を指定して、第二・第三引数に成功時・失敗時のハンドラをそれぞれ与える。
navigator.webkitGetUserMedia( 'video', function(s) {self.successHandler(s)}, function(e) {self.errorHandler(e)} );
カメラからの入力を表示
webkitGetUserMedia成功時のハンドラ内でカメラからの入力をvideoタグのsrcに設定。
successHandler:function(stream) { this.videoElm.src = window.webkitURL.createObjectURL(stream); this.step(); },
ぐにゃぐにゃにする
video要素の表示内容を一時的なcanvasに書きだして表示内容を分割、ImageDataの配列として(デフォルトで)150フレーム分を保持
step:function() { var self = this; var context = document.getElementById('temporaryCanvas').getContext('2d'); context.drawImage(this.videoElm, 0, 0); var stripes = []; for (var i = 0; i < this.frameCount; i++) { var dy = this.height / this.frameCount; stripes.push(context.getImageData(0, dy * i, this.width, dy)); } this.frames[this.initialFrame] = stripes; this.initialFrame = (this.initialFrame + 1) % this.frameCount;
場所ごとに違うフレームのImageData取り出して、表示用のCanvasに貼りつけ。
for (var i = 0; i < this.frameCount; i++) { var frame = this.frames[(this.initialFrame + i) % this.frameCount]; if (frame && frame[i]) this.targetContext.putImageData(frame[i], 0, this.height / this.frameCount * i); } setTimeout(function() {self.step()}, this.interval); }
全部
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Wavy Camera</title> <style> body { background-color:black; } video { display:none; } #canvas { position:absolute; top:50%; left:50%; margin-top:-150px; margin-left:-200px; } .temporary { display:none; } </style> <script> var DelayedFrame = function(opt) { if (!opt) opt = {}; this.initialFrame = 0; this.frameCount = opt.frameCount || 150; this.width = opt.width || 400; this.height = opt.height || 300; this.frames = []; this.videoId = opt.videoId; this.interval = opt.interval || 1; }; DelayedFrame.start = function(opt) { if (navigator.webkitGetUserMedia) { window.addEventListener('load', function() { new DelayedFrame(opt).start(); }); } else { alert('This site rquires WebRTC. Please get Chrome Canary and set MediaStream available.'); } }; DelayedFrame.prototype = { start: function() { if (this.videoId) { this.videoElm = document.getElementById(this.videoId); } else { this.videoElm = document.getElementsByTagName('video')[0]; } this.targetContext = document.getElementById('canvas').getContext('2d'); var self = this; navigator.webkitGetUserMedia( 'video', function(s) {self.successHandler(s)}, function(e) {self.errorHandler(e)} ); }, successHandler:function(stream) { //console.log(stream); this.videoElm.src = window.webkitURL.createObjectURL(stream); this.step(); }, errorHandler:function(error) { console.log(error); }, step:function() { var self = this; var context = document.getElementById('temporaryCanvas').getContext('2d'); context.drawImage(this.videoElm, 0, 0); var stripes = []; for (var i = 0; i < this.frameCount; i++) { var dy = this.height / this.frameCount; stripes.push(context.getImageData(0, dy * i, this.width, dy)); } this.frames[this.initialFrame] = stripes; this.initialFrame = (this.initialFrame + 1) % this.frameCount; for (var i = 0; i < this.frameCount; i++) { var frame = this.frames[(this.initialFrame + i) % this.frameCount]; if (frame && frame[i]) this.targetContext.putImageData(frame[i], 0, this.height / this.frameCount * i); } //window.webkitRequestAnimationFrame(function() {self.step()}); setTimeout(function() {self.step()}, this.interval); } }; DelayedFrame.start(); </script> </head> <body> <video id="video" width="400" height="300" autoplay></video> <canvas id="canvas" width="400" height="300"></canvas> <canvas id="temporaryCanvas" width="400" height="300" style="display:none;"></canvas> </body> </html>
*1:文字列で指定するのはイケテないと思う・・・