msgpackでリアルタイム画像配信

# msgpackとOpenCVを使って、iMACなどのiSightからブラウザに画像をリアルタイム配信
**base64encodedなdataURIより、createObjectURLのが速かったのでメモ**
まぁ、HD画像のdataURIとか長すぎなので当然といえば当然なのですが、Blob経由のimg貼り付けという記事をあまり見つけられなかったので。

## 準備
msgpackのbinary dataをWebSocketを利用して配信する為に以下のコードを利用します。

- [linear-cpp](https://github.com/linear-rpc/linear-cpp)
- [linear-js](https://github.com/linear-rpc/linear-js)

1.opencvをインストール
`$ brew install opencv`
2.linear-cpp/linear-jsをclone, makeなど
<pre>
\## C++
$ git clone --recursive https://github.com/linear-rpc/linear-cpp

\## linear-cpp/sampleのws_server_sample.cppを下記サンプルコードで上書き
$ cp ws_server_sample.cpp linear-cpp/sample
$ cd linear-cpp; ./bootstrap && configure --with-sample && make

$ git clone --recursive https://github.com/linear-rpc/linear-js
\## linear-js/src/linear.debug.jsを下記サンプルコード index.htmlと同じ場所に配置
</pre>

## サンプルコード
### 送信側(C++)
```ws_server_sample.cpp
#include "opencv2/opencv.hpp"

#include "linear/ws_server.h"
#include "linear/group.h"

int main() {
  linear::shared_ptr<linear::Handler> dummy;
  linear::WSServer server(dummy);

  // open cam
  cv::VideoCapture cap(0);
  if(!cap.isOpened()) {
    return -1;
  }
  // starts on INADDR_ANY:37800
  server.Start("0.0.0.0", 37800);
  while (true) {
    // capture image
    cv::Mat frame, resized;
    cap >> frame;
    cv::resize(frame, resized, cv::Size(), 0.5, 0.5); // iMAC, orig: 1280x1024 -> 640x512
    // show image on local window
    cv::imshow("window", resized);

    // jpeg compression
    std::vector<unsigned char> jpeg;
    std::vector<int> param = std::vector<int>(2);
    param[0] = CV_IMWRITE_JPEG_QUALITY;
    param[1] = 95; // 0-100
    cv::imencode(".jpg", resized, jpeg, param);

    // send jpeg data
    linear::Notify notify("image", linear::type::binary(reinterpret_cast<char*>(jpeg.data()), jpeg.size()));
    notify.Send(LINEAR_BROADCAST_GROUP);

    // press 'q' to exit
    int key = cv::waitKey(16); // wait 16ms => 60fps
    if(key == 113) {
      break;
    }
  }
  cv::destroyAllWindows();
  server.Stop();
  return 0;
}
```
## 受信側(HTML + JavaScript)
```index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Jpeg Streaming</title>
    <meta charset='utf-8'/>
    <script type='text/javascript' src='linear.debug.js'></script>
    <script>
      window.onload = function() {
        var transport = {
                         type: 'websocket',
                         host: '127.0.0.1',
                         port: 37800,
                         channel: 'linear',
                        };
        var client = new linear.client({transports: [transport]});
        client.onnotify = function(notification) {
          var blob = new Blob([linear.tobinary(notification.data)], {type: "image/jpeg"});
          document.getElementById('view').src = URL.createObjectURL(blob);
        };
        client.connect();
      };
    </script>
  </head>
  <body id='body'>
    <img id='view'></img>
  </body>
</html>
```

## 確認
<pre>
## ターミナルを開いて、サーバ起動
$ /path/to/ws_server_sample

## 同じPC上のbrowserでindex.htmlを開く。（違うPCからアクセスする場合は、index.htmlのhost部分をサーバを起動させているPCのアドレスやFQDNに書き換え）
$ open /path/to/index.html
</pre>

## ポイント
- **ネットワークから受信したjpegのbinary dataを直接ブラウザのimgタグに貼り付けるには、Blobが有効。**(私の環境では、base64encodedなdataを送信して、imgタグにdataURIとして貼り付けるより10倍くらい速かったです: img.src書き換え前後にconsole.time入れて計測)
- サーバ・クライアント合計70行くらいで実装できて多分、簡単。
- file:: schemeだろうが何だろうが直接CORS可能。(良し悪しですが)
- 今回の場合、msgpackを用いるメリットはないですが、jpegデータとその他情報などを混在させて送受信するようなアプリケーションでは有効なはず。
- WebAudio使えば音声なども送受信&&デコード出来ると思いますが、P2Pでは無いですし、TCPですし、リップシンクとかも大変なので、ちゃんとしたリアルタイムコミュニケーションにはWebRTCのがよいとおもいまうす。