SPOUTでopenFramewoksの映像をvvvvに送信

  • 21
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

概要

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のサンプルで事足りる気もしてます。。。

この投稿は vvvv Advent Calendar 20157日目の記事です。