OBSで画像認識させ、自作のJSアプリとWebsoketで連携する方法

YMSBで試そうと思っていた「配信画面から画像認識で勝者を判定し、自動的にスコアボードに反映させる」ことに成功したので、その方法をドキュメントとして残しておきます。

今回は例としてVFes(VF5REVO、VF5US)で解説していきます。

OBSで画像認識させる

Advanced Scene SwitcherをOBSにインストールする

OBSプラグイン「Advanced Scene Switcher」をインストールしましょう。
このプラグインを使って画像認識を行います。他にもシーンの変更やトラジション、音声などをキーにして色々な動作ができる高機能プラグイン……のようです。筆者は使いこなしきれていません。

Advanced Scene Switcherのダウンロードはこちらから!

Advanced Scene Switcher
This plugin will allow you to automate various tasks using "Macros".Macros consist of a list of conditions under which a...
画面右側の「Go to download」ボタンから最新版のページに飛ぶことができます
お使いのOSを選んでダウンロードしましょう

ダウンロードできたら、インストーラーの場合はダブルクリックでインストールが始まります。
完了後、OBSを起動すると「ツール」に「高機能シーンスイッチャー」というものがあればOK!

※筆者もこのポストを残すにあたり再インストールしたのですが、最新版は日本語対応されてるのですね……1.26.1の時は全部英語だったのに、世の中便利になったものじゃのう。

Advanced Scene Switcherの設定

これは実際の設定画面を見た方が理解が早いでしょう!
「高機能シーンスイッチャー」を開いて、マクロタブから以下の画像のように設定してみましょう。
マクロ名はなんでも構わないです。今回は「VFes_GetR1」としています。
他、ほとんどの項目が日本語で記載されているので、詳細な説明は省略します。

画像は判定させたい画像を自分で用意しましょう。
今回はVFesから、1P側が3ラウンド取得した状態のランプを切り抜いて使っています。
なお、ランプ周りの背景は自分で削除して透明な状態にしているので、マクロ設定でも「パターンのマスクとしてアルファチャンネルを使用します」にチェックを入れています。

画像認識のテスト

画像を指定できたら「ショーマッチ」ボタンをクリック。
実際に動画などを流すと、画像認識のマッチ度を表示してくれます。

マッチしたい場合のシーンのマッチング値を参考に、マクロの「閾値」を調整していきましょう。

マッチしない場合の例。マッチング値が閾値未満の場合はこうなります。
マッチした場合の例。「全体画像は柄と一致します」と出ればOK。

OBSから自前プログラムにWebsocketを出す

OBSのマクロ設定

先ほどのマクロ設定画面の右下部分の設定は「画像がマッチした場合にOBSが行う処理」を示しています。
今回は自作プログラム(YMSBコントローラー)に「GetSet : 1」というメッセージを含めたWebsocketを送信するように設定しています。

OBSのWebSocketサーバー設定

OBSと自作プログラムと連携させるために、OBS側でWebSocketサーバー設定を行います。
「ツール」から「WebSocketサーバー設定」を選択してください。

設定は下の画像の通り。
「WebSocketサーバーを有効にする」と「認証を有効にする」にチェックを入れておきましょう。

自作プログラム側で必要になる「サーバーパスワード」はこの画面では見えませんが、「接続情報を表示」をクリックした先の画面で見えるので、コピーして自作プログラム側で使いましょう。

自作アプリ側のシーケンス

自作アプリ側……YMSBはJavaScriptを使ってコーディングしています。
簡単なシーケンスはこんな感じ。
詳細を解説していますが、概要だけが欲しい場合は次項のコードへGO。

①アプリ→OBSへ接続

プログラム側から「new WebSocket()」でOBSへ接続を作成します。

②アプリ内でイベントハンドラを作成

作成したWebSocketのイベントハンドラを作成。
これでOBS側からメッセージを受信した場合の処理を用意しておきます。

③OBS→アプリへ認証開始

①の直後にOBSから認証開始のメッセージが来ます。
この中にSHA256でハッシュ値を作成するためのソルト値とチャレンジ値が入っているので、その値とOBSのサーバーパスワードを使ってハッシュ値を作成します。

④アプリ→OBSへ認証応答

ハッシュ値と、どのイベントをOBSから受け取るか(eventSubscription)を送信します。
eventSubscriptionの詳細は以下の通り。
今回はAdvanced Scene Switcherからのイベントメッセージを受けたいので、["Vendors"]のビットを立てる = 2^9 = 512を設定する形になります。

/* eventSubscriptions bitpattern
["None"] = 0,
["General"] = 1 << 0,
["Config"] = 1 << 1,
["Scenes"] = 1 << 2,
["Inputs"] = 1 << 3,
["Transitions"] = 1 << 4,
["Filters"] = 1 << 5,
["Outputs"] = 1 << 6,
["SceneItems"] = 1 << 7,
["MediaInputs"] = 1 << 8,
["Vendors"] = 1 << 9,
["UI"] = 1 << 10,
["InputVolumeMeters"] = 1 << 16,
["InputActiveStateChanged"] = 1 << 17,
["InputShowStateChanged"] = 1 << 18,
["SceneItemTransformChanged"] = 1 << 19,
*/

⑤OBS→アプリへ認証完了メッセージ

ハッシュ値が正しいものであれば、OBSからアプリへ認証完了メッセージが送られます。
これで画像認識時のメッセージを受け取れるようになります。

⑦再認証メッセージ

eventSubscriptionsを変更したい場合は再認証メッセージを送ります。
YMSBでは必要ないのでここは省略。

アプリ→OBSへ動作要求

実はOBSのシーンチェンジなどの要求をアプリ→OBSへ送ることもできます。
こちらもYMSBでは実装予定はありませんので省略します。

自作アプリ側のソース

まずはメイン部。
#obsPassに先ほどのWebSocketサーバーのサーバーパスワードを設定しておきます。
OBSのURLはws://127.0.0.1:4455でOK。

let OBS_PASS        = "";
const OBS_URL       = 'ws://127.0.0.1:4455'
let OBS_SOCKET      = "";

// ■■ OBS連携開始 ■■

const startOBSLink = () => {
    if ("" == jQuery("#obsPass").val()) {
        // error
    }
    else {
        OBS_PASS        = jQuery("#obsPass").val();
        OBS_SOCKET      = new WebSocket(OBS_URL);

        OBS_SOCKET.addEventListener("message", (event) => {
            const rcvData = JSON.parse(event.data);
            console.log("rcv[Message] : ", rcvData);

            if (0 == rcvData.op) {
                const rcvSalt   = rcvData.d.authentication.salt;
                const rcvChal   = rcvData.d.authentication.challenge;
                const rcvRpcv   = rcvData.d.rpcVersion;
                sendIdentify(rcvSalt, rcvChal, rcvRpcv);
            }
            else if (5 == rcvData.op) {
                if (512 == rcvData.d.eventIntent) {
                    const msg = rcvData.d.eventData.eventData.message;
                    console.log("rcv[message] : " + msg);
                    switch (msg) {
                        case "GetSet : 1" :
                            sumScore(0);
                            break;
                        case "GetSet : 2" :
                            sumScore(1);
                            break;
                        default:
                            break;
                    }
                }
            }
        });
    }
}

続いてSHA256でハッシュ化する処理。
これを実装するにあたって「jsSHA」というライブラリを使わせてもらいました。

const EVENT_SUB     = 512;
const sendIdentify = (rcvSalt, rcvChal, rcvRpcv) => {
    const salt      = rcvSalt;
    const challenge = rcvChal;
    const pass      = OBS_PASS;

    const textHash = new jsSHA("SHA-256", "TEXT");
    textHash.update(pass);
    textHash.update(salt);

    const hash = textHash.getHash("B64");
    const textHash2 = new jsSHA("SHA-256", "TEXT");
    textHash2.update(hash);
    textHash2.update(challenge);

    const resp = textHash2.getHash("B64");
    const sendData =
    {
        "op": 1,
        "d": {
            "rpcVersion": rcvRpcv,
            "authentication": resp,
            "eventSubscriptions": EVENT_SUB
        }
    };

    sendToOBS(sendData);
}

const sendToOBS = (sendData) => {
    const sendDataJSON = JSON.stringify(sendData, null, 2);
    OBS_SOCKET.send(sendDataJSON);
    console.log("sendToOBS : ", sendData);
}

最後に画像認識メッセージを受信したときにスコアをカウントアップする処理sumScore()を実装して完了。ここは任意の処理になるのでソースコードは割愛します。

参考文献

先人たちの経験と情報展開に大いなる感謝を。

スマブラ大会の配信に、シーンを自動で切り替える機能を導入する方法|taguyan/たぐやん
スマブラ大会のYoutube配信で、画像認識を用いてシーンを自動で切り替える機能を導入しました。 スマブラ大会の配信に、シーンを自動で切り替える機能を導入しました。 試合開始時に対戦画面に、試合終了時に会場カメラになります。 既に数十時間運...
OBSとWebSocketを使って通信するプログラムを作る時に知っておいて損がない事
配信ツールで有名なOBS Studioですが、WebSocketを使うと外部の自作プログラムから、OBSを操作できるようになります。この記事は、これからOBSとの連携するアプリをプログラミングで作ろうとしている私と同様にプログラム初心者向け...
OBS の obs-websocketプラグインの認証に関する仕様・試したことのメモ - Qiita
はじめに先日、以下の記事でも書いた OBS を遠隔制御することができるプラグイン「obs-websocket」の話です。●OBS をスマホや M5GO(M5Stack)から遠隔制御 〜 MQTT…
JavaScriptでハッシュ関数(SHA256)を使う - Qiita
はじめにJavaScriptでハッシュ関数を使いたくて、いろいろな記事を見ていたのですが、node.jsでインポートして使うのが多かった印象でした。しっかりとは私も理解していないのですが、Jav…
PAGE TOP
タイトルとURLをコピーしました