##はじめに
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)でブラウザで表示されます。
次に、例えば openFrameworks
のofVideoGrabber
を用いて、内蔵されてるカメラ (Webカメラでも)から取得した映像を使いたいと思います。
その時以下のようにサンプルを書き換えました。
#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;
};
#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の場合、環境設定>ネットワークから
IPアドレス (赤の資格で隠しましたが) が指定されています。
と書かれているところをコピーして持って来れば大丈夫です。
実行してみて、同じネットワーク内の他のPCのブラウザで
XXX.XXX.XX.XX:7980
を開くと、以下のようになるはずです。(正確には http://XXX.XXX.XX.XX:7890/)
配信して、それをブラウザで見るだけでしたらここで終了です!
次の、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
を元々使いたかったので、OpenCV
のcv::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;
}
でできるみたいですが、これをopenFrameworks
で使いたいので、少し変更します。
またOpenCV
がinstallされていない方はinstallしてください!
macports
が入っている方は
sudo port install opencv
でできます。この辺りはたくさん参考となる記事が出てますので、そちらにお任せします。
Xcode
で使おうとする場合、追加するフレームワークとかが若干厄介なので参考に
http://qiita.com/Hiroki11x/items/d57f015bbad03b978403
ここで新しいプロジェクトを作成してください。特にaddon等の追加は必要ないです。以下の手順でプロジェクトファイルを作っていきます。
- 適当なフレームワークなどを
Link Binary With Libraries
から追加
-
Header Search Path
/Library Search Path
を設定
こちらも
http://kzm42.blog24.fc2.com/blog-entry-25.html
が参考になりました
- ヘッダファイルの定義
以下のように書きました。
cv::VideoCapture
で映像を取得、cv::Mat
に変換後、ofImage
にpixel
をセット
ofImage
自体をdraw
するという作戦です。
#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;
};
- 実装ファイルを定義
#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