LoginSignup
17
19

More than 5 years have passed since last update.

openFrameworksで動画通信してみた

Last updated at Posted at 2016-06-08

はじめに

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側の作成

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

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

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

a.gif

次に、例えば openFrameworksofVideoGrabberを用いて、内蔵されてるカメラ (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

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

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

13393056_1048106968604817_852110919_n.jpg

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

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

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

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

から

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

に変更しておきます!

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

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

まず、今回、他の事情で、OpenCVを元々使いたかったので、OpenCVcv::VideoCapture()を用いて、動画を取得します。
このcv::VideoCapture()はhttpで公開されている動画ファイルを開くことができます。

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

#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が入っている方は

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に変換後、ofImagepixelをセット
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;
};

  1. 実装ファイルを定義
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

17
19
0

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
17
19