28
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

vvvvAdvent Calendar 2015

Day 7

SPOUTでopenFramewoksの映像をvvvvに送信

Last updated at Posted at 2015-12-07

#概要
vvvvでopenFrameworksやProcessingからの映像を受け取って
出力したいという場合、SPOUT(http://spout.zeal.co/) を使います。
MacでいうところのSyphon(http://syphon.v002.info/) ですね。
SPOUTを使うことで他の言語で生成した映像をvvvvで受信したり、
逆にvvvvで作った映像をopenFrameworksなどに送ることがが可能になります。

この記事ではopenFrameworksで生成した映像をSPOUTを使って送信しvvvvで受信する方法を説明します。

#全体図
システムの構成は以下になります。
WS000754.JPG

送信側のopenFrameworksではSPOUTのDLLを使って映像を送信し、
受信側のvvvvではSpout_Receiver.v4pを使って映像を受信します。
vvvvは映像の送信も受信も簡単なんですがopenFrameworksはちょっと面倒でした。
(私がopenFrameworksにあまり詳しくないからかもですが。)

#準備 SPOUTのダウンロードとインストール
Spoutのダウンロードは http://spout.zeal.co/ のDownloadをクリックし
SpoutSetup_V2.004-beta.zipを入手します。

WS000731.JPG

ZIPファイルを任意のフォルダに解凍しSpoutSetup_V2.004-beta.exeを実行すると
インストールが始まります。
インストール先はどこでもOKです。展開されたファイルを使うだけなので。
(※イントールの画像は抜粋です。)

WS000732.JPG
WS000733.JPG
WS000734.JPG
WS000736.JPG

インストール先のフォルダを確認するとSpout2のフォルダが以下のように展開されています。

WS000738.JPG

この後に使うのは送信側がSpout2/SPOUTSDK/SpoutSDK/Binaries にあるSpout2.dll、Spout2.lib、SpoutDLL.hです。

WS000741.JPG

受信側はSpout2/VVVV/Receiver/DirectX 9(11でもOK)にあるパッチファイルになります。

WS000756.JPG

openFrameworksの実装については、\Spout2\SPOUTSDK\SpoutSDK\SPOUTDLL\cbSpoutExample\srcのソースをコピペし使います。
これらの場所を覚えておいてください。

WS000757.JPG

#openFrameworks(0.9.0)でのSPOUT(Sender)の設定(Visual Studio 2015編)
openFrameworksでは新しいプロジェクトを作成した状態から説明します。
openFrameworksはとっかかりは http://yoppa.org/ をみればすべて書いてますのでそちらを参考にしてください。偉大です。

次にSpoutを使うための準備をします。Spout2.dll、Spout2.libをopenFrameworksのプロジェクトのbinフォルダにコピーします。
また、SpoutDLL.hをsrcフォルダにコピーします。

SpoutDLL.h をプロジェクトに追加します。
SolutionExplorerのsrcで右クリックし、Add>Existing Item で先ほど配置したSpoutDLL.hを選択すればOKです。
WS000746.JPG

こんな感じでSolutionExplorerにSpoutDLL.hが追加されます。

WS000759.JPG

次にSpout2.lib をLinkerに追加します。
Project>Vs2015Test(※プロジェクト名)PropertiesでPropertyPagesを開き
LinkerのInputを選択しAdditional Dependenciesでを選択し「.\bin\Spout2.lib」を
テキストボックスに記載し、OKをクリックします。

WS000758.JPG

WS000752.JPG

これで準備は整いました。

#ofApp.hの実装

次に実装です。
main.cppは特別編集は必要ありません。
ofApp.hではSpoutDLL.hをインクルードします。

#include "SpoutDLL.h" // for Spout2 dll

また、ofApp.cppで使う変数を定義しておきます。
ソースはインストール先の\Spout2\SPOUTSDK\SpoutSDK\SPOUTDLL\cbSpoutExample\src\ofApp.hの一部をコピペすればよいです。

void exit();
bool InitGLtexture(GLuint &texID, unsigned int width, unsigned int height);
char sendername[256];			// Shared memory name
GLuint sendertexture;			// Local OpenGL texture used for sharing
bool bInitialized;			// Initialization result
ofImage myTextureImage;			// Texture image for the 3D demo
float rotX, rotY;

#ofApp.cppの実装
次にofApp.cppの実装です。
\Spout2\SPOUTSDK\SpoutSDK\SPOUTDLL\cbSpoutExample\src\ofApp.cppを参考に
必要なコードをコピペします。
まずはnamespaceを記載します。

// ====== SPOUT =====
using namespace Spout2;

次にsetup()の実装ですが以下のコードをサンプルからコピペします。
sendernameの文字列はvvvvと共通の文字列にする必要がありますが
適当でOKです。

// ====== SPOUT =====
bInitialized = false;		        // Spout sender initialization
strcpy(sendername, "CB Spout Sender");	// Set the sender name
ofSetWindowTitle(sendername);			// show it on the title bar
										// Create an OpenGL texture for data transfers
sendertexture = 0; // make sure the ID is zero for the first time
InitGLtexture(sendertexture, ofGetWidth(), ofGetHeight());
// Set the window icon from resources
//SetClassLong(GetActiveWindow(), GCL_HICON, (LONG)LoadIconA(GetModuleHandle(NULL), MAKEINTRESOURCEA(IDI_ICON1)));
// ===================

次にdraw()の実装です。

こちらは、SPOUTで送信したい範囲を
CreateSenderとSendTextureで囲む形になります。

//--------------------------------------------------------------
void ofApp::draw(){

    char str[256];
    ofSetColor(255);

    // ====== SPOUT =====
    // A render window must be available for Spout initialization and might not be
    // available in "update" so do it now when there is definitely a render window.
    if (!bInitialized) {
        bInitialized = CreateSender(sendername, ofGetWidth(), ofGetHeight()); // Create the sender
    }
    // ===================
    //ここから

    ofDrawCircle(100.0, 100.0, 10.0, 15.0);

    //ここまでが送信される。
    // ====== SPOUT =====
    if (bInitialized) {

        if (ofGetWidth() > 0 && ofGetHeight() > 0) { // protect against user minimize

                                                     // Grab the screen into the local spout texture
            glBindTexture(GL_TEXTURE_2D, sendertexture);
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, ofGetWidth(), ofGetHeight());
            glBindTexture(GL_TEXTURE_2D, 0);

            // Send the texture out for all receivers to use
            SendTexture(sendertexture, GL_TEXTURE_2D, ofGetWidth(), ofGetHeight());

            //ここからの映像は送信されないのでUIとかを表示させてもいいかも

            // Show what it is sending
            ofSetColor(255);
            sprintf(str, "Sending as : [%s]", sendername);
            ofDrawBitmapString(str, 20, 20);

            // Show fps
            sprintf(str, "fps: %3.3d", (int)ofGetFrameRate());
            ofDrawBitmapString(str, ofGetWidth() - 120, 20);

        }
    }
    // ===================
}

あとはwindowResized、exit、InitGLtextureをサンプルからコピペして上書きすると
完了です。

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h) {
	// ====== SPOUT =====
	// Update the sender texture to receive the new dimensions
	// Change of width and height is handled within the SendTexture function
	if (w > 0 && h > 0) // protect against user minimize
		InitGLtexture(sendertexture, w, h);

}

//--------------------------------------------------------------
void ofApp::exit() {

	// ====== SPOUT =====
	ReleaseSender(); // Release the sender

}

// ====== SPOUT =====
bool ofApp::InitGLtexture(GLuint &texID, unsigned int width, unsigned int height)
{
	if (texID != 0) glDeleteTextures(1, &texID);

	glGenTextures(1, &texID);
	glBindTexture(GL_TEXTURE_2D, texID);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);

	return true;
}

ちょっと難しいのは最初のSpoutDLL.libの設定とか配置ですかね。。。
結構苦労しました。
コンパイルエラーの連続ですわ。
実装はコピペでいいので特に問題はないですかね。

#vvvvでSPOUT(Reviever)
次に受信側のvvvvの実装です。こちらはすこぶる簡単。
Spout2/VVVV/Receiver/DirectX 9/DX9_ReceiverExample.v4pを実行し、Spout_Receiverの
SpoutSenderNameの文字列を送信側の文字列を同じにするだけで受信できます。
今回はサンプルと同じく「CB Spout Sender」に変更してます。
vvvvではTextureが受信できるのであとはFullscreenQuadをかましてRendererにつなげれば
openFrameworksからの映像をvvvvで表示できます。
私は、vvvvでは手軽にエフェクトを変更したり出来るのでメインの映像はopenFrameworksでつくって
エフェクトとか画像の切り替えはvvvvでやるみたいな使い方をしてます。
全部vvvvでやればいいじゃないかって話ですが、まだまだ未熟で描きたい絵がvvvvで描けないので。
あと、openFramewoksでshaderを使った映像をvvvvに送るみたいな使い方をしてます。
するとGLSLSandBoxみたいなGLSLの映像もvvvvで扱えるようになるので。
(HLSLに移植したらいいじゃないって話ではありますが・・・。)

実装はこんな感じです。折角なんでopenFrameworksのHelloWorldともいえる(かどうかは知らんが)
3DPrimitivesExampleの映像をvvvvに送信して表示してみましょう。
vvvvのエフェクトをかけて最終的に出力してます。

WS000760.JPG

こんな感じで、openFrameworksの映像をvvvvに送れるわけです。
ただ、送信側のウィンドウを最小化すると映像が止まってしまうとか、
送信側のウィンドウはプレビュー的に小さくして実際に送る映像は1280×800に
したいんだけどなーという感じがあるんですが解決方法がわからない。
SPOUT、oFに詳しい人がいたら教えてください。

#まとめ
vvvvがかなりマニアックなのにもかかわらず、さらにマニアックなSPOUTについての記事を書きました。需要は無いかもしれませんが、Windows環境ではSyphonは使えないので、openFrameworksのコンテンツをvvvvで活用したい人(そんな人いるかしら・・・)なんかのお役に立てれば幸いです。
どちらかというとopenFrameworksの記事ですか?みたいな話しもありますが・・・(汗
最終的にはvvvvを使ってるので良しとシテクダサイ。

あと、手さぐりで書いているので、SPOUT導入の正当なやりかたじゃないような気がしてます。
色々、ご指摘いただけると幸いです。また、質問があればどうぞ。

要望があればサンプルコードをGitHubにあげる・・・かもですが、SPOUTのサンプルで事足りる気もしてます。。。

28
25
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
28
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?