hakobera's blog

技術メモ。たまに雑談

WebSocket で PNG 画像をバイナリ転送して、JavaScript で展開して表示してみた

Node.js で WebSocket-Node を使って実装しました。
転送するめぼしい画像が見当たらなかったので、デスクトップをスクリーンキャプチャして転送してみました。

ブラウザはChrome 17以上か、Firefox 11以上が必要です。サーバ側は scrrencapture コマンドを利用している関係で Mac OS X限定です。

デモ

上半分が転送元のデスクトップ、下半分が転送された画像をブラウザで表示したものです。ニコ動のコメントの飛び具合を見るとわかると思いますが、800*600の解像度の画像を、横640に縮小して転送して、1FPSくらいです。(※ これはWebSocket の限界ではありません。速度は向上させる余地はかなりありますが、今回の本質ではないので気にしないことにします)

ソースは github に置いてあります。
hakobera/screencast · GitHub

解説みたいなもの

今回の実装の面白い点は表題の通り「バイナリ転送した PNG 画像を、JavaScript で展開して、Canvas に表示していること」です。

PNG画像の展開には png.js を利用しています。

※コメントで教えてもらった URL.createObjectURL() で描画する方法の方が、クライアントのCPU負荷が低く(Thinkpad X61(Core2Duo 2GHz) Windows 7Chrome 16 で 25% -> 5%)なりました。ブラウザがサポートしている形式を展開する場合は blob 形式でデータを受信して、URL.createObjectURL()で生成したURLをImageで描画するのが良いのではないでしょうか。なお、URL.createObjectURL版のソースコードは blob ブランチに置いてあります。

WebSocket で画像を転送する方法(正確には画像を連続で送信して動画のように表示させる方法)は、今回のも含めて自分が試しただけでも4つあります。

それぞれの現時点での利点と欠点は以下だと思っています。

実装方法 利点 欠点 Chrome FireFox Safari iOS IE9 IE8 IE6
バイナリ(圧縮形式)で転送して、展開して表示 送信データ量が小さい arraybuffer/blog サポートが必要 × × × × ×
バイナリ(RAW)で転送して、そのまま表示 クライアント側の展開がいらないので、クライアント負荷が小さい 送信データ量が極大/arraybuffer/blog サポートが必要 × × × × ×
Data URI(文字列)で転送して、そのまま表示 Safari、Mobile WebKitでも使える 送信データ量が大きい(バイナリ比+33%) ×
画像URLを送信して、ブラウザでロードして表示 送信データ量が極小/Safari、Mobile WebKitでも使える 別途画像配信用のHTTPサーバが必要

IEはWebSocketをサポートしていないので、Socket.IO を使えばできる箇所は△にしています。

ブラウザサポートは次第に解消されていくと思うので、将来的にはバイナリ転送、JSで展開という形に落ち着いていくんじゃないのかな、と思いました。場合によっては独自の圧縮プロトコルも使えるので、高圧縮、暗号化など、色々と応用範囲がありそうですし。

また、WebSocketが gzip 圧縮をサポートするらしいので、画像が転送できれば良いという用途では、RAW形式も生き残るかもしれないですね。