LoginSignup
8
7

More than 5 years have passed since last update.

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

Posted at

msgpackとOpenCVを使って、iMACなどのiSightからブラウザに画像をリアルタイム配信

base64encodedなdataURIより、createObjectURLのが速かったのでメモ
まぁ、HD画像のdataURIとか長すぎなので当然といえば当然なのですが、Blob経由のimg貼り付けという記事をあまり見つけられなかったので。

準備

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

1.opencvをインストール
$ brew install opencv
2.linear-cpp/linear-jsをclone, makeなど


## 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と同じ場所に配置

サンプルコード

送信側(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>

確認


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

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

ポイント

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7