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 7 の Chrome 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形式も生き残るかもしれないですね。