openFrameworksで動画通信してみた

##はじめに
openFrameworksで一方から、もう一方のプロジェクトへ映像が送りたかったのでプロトタイプを作ってみました。
ただ、送信元はどうなるかわからないので(oF以外を使う可能性もあるので)、汎用性の高そうなものを目指しました。
こここう使っちゃダメだよとか、これはこうしたほうがいいよ、などたくさんあると思うので教えて下さい🙇🙇🙇

こんなのあるじゃんと、後になってきづきました(oF to oFって決まってる場合はこちらの方が綺麗)
http://cvl-robot.hateblo.jp/entry/2016/05/24/193639

##下準備
https://github.com/bakercp/ofxHTTP
https://github.com/bakercp/ofxIO
https://github.com/bakercp/ofxMediaType
https://github.com/bakercp/ofxSSLManager
https://github.com/bakercp/ofxTaskQueue
https://github.com/bakercp/ofxNetworkUtils

を`addons`下にcloneしておきます

##Server側の作成
https://github.com/bakercp/ofxHTTP/tree/master/example_basic_ip_video_server

を元に`ProjectGenerator`でsrc類をimportし、プロジェクトを作成。
多分そのままサンプルが動くはずです。
(ブラウザとかから動画が見れるようになる)

ただしこれはローカルなので、同じネットワーク内の他のPCブラウザとかから見れるようにするためには、適当な`port`と`host`の設定が必要です！
(Wi-Fiのルーターの設定上どうのこうのみたいな問題はケースバイケースです、、、)

実行するとこのような感じ
http://127.0.0.1:7890/ みたいなlocalhost(現在使用しているシステムを指す。 これは、IPv4では127.0.0.1)でブラウザで表示されます。


![a.gif](https://github.com/Hiroki11x/Gif_Animations/raw/master/qiita/001.gif)

次に、例えば `openFrameworks`の`ofVideoGrabber`を用いて、内蔵されてるカメラ (Webカメラでも)から取得した映像を使いたいと思います。

その時以下のようにサンプルを書き換えました。


```ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxHTTP.h"

class ofApp: public ofBaseApp{
public:
    void setup();
    void update();
    void draw();

    ofx::HTTP::SimpleIPVideoServer server;
    ofVideoGrabber mVideoGrabber;
};
```


```ofApp.cpp


#include "ofApp.h"


void ofApp::setup()
{
    ofSetFrameRate(30);

    mVideoGrabber.setVerbose(true);
    mVideoGrabber.initGrabber(320*3, 240*3,OF_IMAGE_COLOR);

    ofx::HTTP::SimpleIPVideoServerSettings settings;

    // Many other settings are available.
    settings.setPort(7890);
    settings.setHost("XXX.XXX.XX.XX");//このPCのIPアドレス

    // The default maximum number of client connections is 5.
    settings.ipVideoRouteSettings.setMaxClientConnections(3);

    // Apply the settings.
    server.setup(settings);

    // Start the server.
    server.start();

#if !defined(TARGET_LINUX_ARM)
    // Launch a browser with the address of the server.
    ofLaunchBrowser(server.getURL());
#endif

}


void ofApp::update(){
    // Update the video player.
    mVideoGrabber.update();

    if(mVideoGrabber.isFrameNew()){
        server.send(mVideoGrabber.getPixels());
    }
}


void ofApp::draw(){
    mVideoGrabber.draw(0,0);

    // Display the number of connected clients for reference.
    std::stringstream ss;

    ss << "Num clients connected: ";
    ss << server.getNumConnections();

    ofDrawBitmapStringHighlight(ss.str(), 20, 20);
}

```

この時、このPCのIPアドレスとかいたところには、OSXの場合、環境設定>ネットワークから


![スクリーンショット 2016-06-08 19.09.50.png](https://qiita-image-store.s3.amazonaws.com/0/56510/bfd2fad5-8a93-a4f6-90a1-4e963502bb87.png)

IPアドレス (赤の資格で隠しましたが) が指定されています。
と書かれているところをコピーして持って来れば大丈夫です。

実行してみて、同じネットワーク内の他のPCのブラウザで
XXX.XXX.XX.XX:7980
を開くと、以下のようになるはずです。(正確には http://XXX.XXX.XX.XX:7890/)

![13393056_1048106968604817_852110919_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/56510/95f43432-f85b-dfc0-dcef-f66e6f59c69c.jpeg)

配信して、それをブラウザで見るだけでしたらここで終了です！

次の、`openFrameworks`で配信した動画を取得するための下準備をしておきます。
次の章で説明するやり方では`OpenCV`を用いて取得するので、動画の形式を`.mjpg`にしておくと都合がいいのです。そのため

addons/ofxHTTP/libs/ofxHTTP/src/IPVideoRoute.cppの122行目(おそらく)
を

```cpp
const std::string IPVideoRouteSettings::DEFAULT_VIDEO_ROUTE = "/ipvideo";
```

から

```cpp
const std::string IPVideoRouteSettings::DEFAULT_VIDEO_ROUTE = "/ipvideo.mjpg";
```

に変更しておきます！


##配信した映像を他の`openFrameworks`プロジェクトで利用する

やり方が上手くないかもしれませんが(もっと上手くやれるよ！って方、記事書いて欲しいです😭😭😭)、openCVを用いた、方法を紹介します。

まず、今回、他の事情で、`OpenCV`を元々使いたかったので、`OpenCV`の[cv::VideoCapture()](http://docs.opencv.org/3.1.0/d8/dfe/classcv_1_1VideoCapture.html#gsc.tab=0)を用いて、動画を取得します。
この[cv::VideoCapture()](http://docs.opencv.org/3.1.0/d8/dfe/classcv_1_1VideoCapture.html#gsc.tab=0)はhttpで公開されている動画ファイルを開くことができます。

ただopenCVで取得するだけなら、

```cpp
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

#ifdef _DEBUG
#pragma comment(lib, "E:\\opencv300\\build\\x86\\vc11\\lib\\opencv_world300d.lib")
#else 
#pragma comment(lib, "E:\\opencv300\\build\\x86\\vc11\\lib\\opencv_world300.lib")
#endif

int main(int argc, char* argv[])
{
	Mat frame;
	namedWindow("video", 1);
	VideoCapture cap("http://127.0.0.1:7890/ipvideo.mjpg");
	while ( cap.isOpened() )
	{
	    cap >> frame;
	    if(frame.empty()){
			cout << "empty" << endl;
			break;
		}

	    imshow("video", frame);
	    if(waitKey(30) >= 0) break;
	}   

	return 0;
}
```
> 引用 https://gist.github.com/dotchang/3a5250b438da055626c8

でできるみたいですが、これを`openFrameworks`で使いたいので、少し変更します。
また`OpenCV`がinstallされていない方はinstallしてください！

`macports`が入っている方は

```bash
sudo port install opencv
```

でできます。この辺りはたくさん参考となる記事が出てますので、そちらにお任せします。
`Xcode`で使おうとする場合、追加するフレームワークとかが若干厄介なので参考に
http://qiita.com/Hiroki11x/items/d57f015bbad03b978403

ここで新しいプロジェクトを作成してください。特にaddon等の追加は必要ないです。以下の手順でプロジェクトファイルを作っていきます。

1. 適当なフレームワークなどを`Link Binary With Libraries`から追加
> http://kzm42.blog24.fc2.com/blog-entry-25.html
が参考になりました

2. `Header Search Path` / `Library Search Path`を設定
> こちらも
> http://kzm42.blog24.fc2.com/blog-entry-25.html
が参考になりました

3. ヘッダファイルの定義

以下のように書きました。
`cv::VideoCapture`で映像を取得、`cv::Mat`に変換後、`ofImage`に`pixel`をセット
`ofImage`自体を`draw`するという作戦です。

```ofApp.h

#pragma once

#define CAM_WIDTH 960
#define CROP_HEIGHT 720

#include <opencv2/opencv.hpp>
#include "ofMain.h"

class ofApp: public ofBaseApp{
public:
    void setup();
    void update();
    void draw();

    cv::VideoCapture cap;
    cv::Mat frame;
    
    ofImage img;
};

```


4. 実装ファイルを定義

```ofApp.cpp
#include "ofApp.h"


void ofApp::setup(){
    ofSetFrameRate(0);
    cap = cv::VideoCapture("http://XXX.XXX.XX.XX:7890/ipvideo.mjpg");
    img.allocate(CAM_WIDTH, CROP_HEIGHT, OF_IMAGE_COLOR);
}


void ofApp::update(){
    if ( cap.isOpened() ){
        cap >> frame;
        if(frame.empty()){
            cout << "empty" << endl;
            exit();
        }else{
            img.setFromPixels(frame.ptr(), frame.cols, frame.rows, OF_IMAGE_COLOR,FALSE);
            /*
             
             openFrameworks/graphics/ofImage.cpp内を以下に書き換え
             自作のサンプルから映像を飛ばす場合、このように取得する必要あり

             template<typename PixelType>
             void ofPixels_<PixelType>::swapRgb(){
             switch(pixelFormat){
             case OF_PIXELS_RGB:
             case OF_PIXELS_BGR:
             case OF_PIXELS_RGBA:
             case OF_PIXELS_BGRA:{
             for(auto pixel: getPixelsIter()){
             std::swap(pixel[3],pixel[0]);//<-自作のserverプロジェクトを使う場合はこのswapに変更
             }
             }
             */
        }
        if(cv::waitKey(30) >= 0) exit();
    }
}


void ofApp::draw(){
    if(img.isAllocated()){
        img.draw(0,0);
    }
    ofSetWindowTitle("FPS: "+ofToString(ofGetFrameRate()));
}
```

これにてようやく通信された映像をopenFrameworksで使うことができます！

ソースコード内にコメントで書きましたが、先ほど作ったserver側のプロジェクトのまま通信すると、`ofImage::setFromPixels`関数内の`swap()`の要素を書き換えないと変なことになってしまったので一旦書き換えました。


##参考にしたサイトなど

openFrameworksを受信側も使うこと以外はほとんどこれを参考にしました。ただしWindows向け？？
http://cvl-robot.hateblo.jp/entry/2014/11/28/182130

OpenCV,openFrameworks間のofImageとかcv::Matの変換とかに関して
http://izmiz.hateblo.jp/entry/2015/03/02/214219

今回使ってないけど、双方向ならこちらが便利そう
http://cvl-robot.hateblo.jp/entry/2014/11/28/182130