5
5

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 1 year has passed since last update.

【Zoom Meeting SDK】TouchDesignerによるZoomオペレーションシステム

Last updated at Posted at 2022-07-10

1. はじめに

この記事は「Qiita Engineer Festa 2022」に参加するための記事です。

初めまして。
株式会社レイという映像機材を扱う会社でエンジニアをしています。

普段はTouchDesignerやUnrealEngineを使って、
映像オペレーションのシステムやXR表現の研究・開発をしています。

今回はZoomAPI/SDKとTouchDesignerを組み合わせた、
Zoomオペレーションシステムをプロトタイピングしました。

2.システム環境

  • Windows10
  • Geforce RTX 3060
  • node: v16.15.1
  • npm: 8.11.0
  • TouchDesinger: 2022.25370
  • UnrealEngine: 4.27.2

3. 映像屋が抱えるZoomオペの課題

私たちが企業の表彰式などの現場でオペレーションをする時、
表彰者の映像を扱うためZoomを利用することが多くあります。

その際表彰者をピン留めして不要な部分をクロップし、
メインオペレーターのスイッチャーなどに出力します。

しかし、この方法だと表彰者の数だけピン留めが必要ですし、
PCごとにZoomの設定を毎回の現場でする必要がありました。
またその分作業員も必要なので、物的・人的コストが掛かります。

(ズラァ...)
zoomEX.png
この課題を、ZoomAPI/SDKを活用することで解決できないかと考えました。

4. ZoomAPI/SDKの機能選定

初めはRaw Videoが取得できるというVideo SDKの利用を検討しました。
しかし、Video SDKは通常のミーティングとの互換性が無いようです。

VideoSDKで実現できる映像音声通話は個別の通信としての扱いになるため通常のZoomミーティングやウェビナーとは互換性がありません。そのため、VideoSDKで生成された通話識別子(Session Name)へは通常のZoomクライアント及びMeetingSDKからは参加接続できないものとなります。

なので、通常のミーティングにも参加できるMeeting SDKを利用する方針にしました。

5. MeetingSDKを利用した映像の取得

今回はプロトタイピングですので、公式のサンプルをそのまま利用します。
参考にしたのは、こちらの記事とGithubのリポジトリです。

導入方法は上記記事とREADMEのままなので、割愛します。

npm startすると127.0.0.1:9999で入室前の画面が立ち上がるので、
事前に立ち上げた部屋の番号とパスワードを入力して、Joinを押します。
(必要に応じて名前の変更と言語を日本語に設定して下さい)

URLはJoinを押すたびに異なるものが発行されるので、
これを必要な分だけ繰り返せば、1台のPCで複数の人を管理することが出来ます。

別タブで立ち上がったURLを、Pallete/ToolsにあるWeb Browserに入力します。
Web Render TOPだと画面が操作出来ないので、Web Browserを使います)
これでZoomミーティングの部屋をTouchDesigner内で操作が出来るようになります。
webB.png
URLは手入力でベタ打ちしても良いのですが、数が多いと面倒なので、
Fetch APIを使ってTouchDesignerに直接渡します。(Fetch APIの理由は後述)

6. FetchAPIによるURLの送受信

発行されたURLは、http通信でTouchDesignerに渡します。
httpXMLHttpRequestを試しましたが、CORSに弾かれてしまいました。

HTTPレスポンスヘッダにAccess-Control-Allow-Originを付加すれば
通せるようなのですが、今回の検証段階では上手くいきませんでした。

そこでfetch関数の引数にmode: "no-cors"を加えたところ、回避出来ました。
以下、サンプルのコードjs/index.jsの103行目~に追記した箇所になります。

index.js
      var signature = ZoomMtg.generateSDKSignature({
        meetingNumber: meetingConfig.mn,
        sdkKey: SDK_KEY,
        sdkSecret: SDK_SECRET,
        role: meetingConfig.role,
        success: function (res) {
          console.log(res.result);
          meetingConfig.signature = res.result;
          meetingConfig.sdkKey = SDK_KEY;
          var joinUrl = "/meeting.html?" + testTool.serialize(meetingConfig);
          console.log(joinUrl);
          // 別タブが開く必要が無いので、コメントアウト
          // window.open(joinUrl, "_blank");

          jsonData = {key: joinUrl};
          postUrl  = 'http://127.0.0.1:9980';

          fetch( postUrl, {
          method: 'POST',
          mode: 'no-cors',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(jsonData),
          })
          .then(response => response.json())
          .then(data => {
            console.log('Success:', jsonData);
          })
          .catch((error) => {
            console.error('Error:', error);
          });
        },
      });

普段JavaScriptは全く書かないので、書き方はMDN Web Docsの通りです。

あくまでローカルネットワーク内でURLを渡すだけなので問題無いと思いますが、
本番環境では、CORSの仕組みをしっかり理解した上で実装します。(自戒)

POSTしたURLは、TouchDesignerのWeb Server DATで受け取ります。
以下、Web Server DATに紐づいているコールバックのオペレータに追記した箇所です。

webserver1_callbacks
import json

# URLを保持するためのTable DAT
table = op("table1")

def onHTTPRequest(webServerDAT, request, response):

	response['statusCode'] = 200 # OK
	response['statusReason'] = 'OK'
	response['data'] = '<b>TouchDesigner: </b>' + webServerDAT.name
	
	get_data = request['data']
	load_data = json.loads(get_data)
	value_data = load_data['key']

	table.appendRow("http://127.0.0.1:9999" + value_data)
	
	return response

後はTable DATに入るURLをWeb Browserにリファレンスすれば完了です。

7. TouchDesingerでの映像演出

せっかくTouchDesignerを使っているので、
簡単なインタラクティブ演出を加えていきます。

Face Tracking CHOPを使って顔の上に王冠を乗せ、
賑やかしのパーティクルなんかも加えて派手にします。
素材はいらすとやから拝借。(王冠おじいちゃん

全体のプログラム図 ↓↓↓
system.png
Face Tracking CHOPは、内部的にNvidia MaxineのAR SDKを利用しており、
顔のポイントや向きなどを手軽に取得することが出来るオペレータです。
ここで取得したUVwidthの数値を基準に、顔の位置に合うよう調整します。

詳しくは以下チュートリアル動画を参照して下さい。

次にblenderを開き、紙吹雪風のパーティクルを作ります。
こちらも今回は以下チュートリアル動画の通りなので、詳しくは(以下略)。

ここでちょっとしたTIPSです。
Channel Mix TOPを使うと、黒背景の画像や動画を透過背景に出来ます。
方法はパラメータをalpha1 => 1、alpha4 => 0にするだけです。
(追記:alpha1 => 1、alpha2 => 0, alpha4 => 0の方が良い)
confetti.png
この画をSpout Out TOPでUnrealEngineに渡してテクスチャに使い、
さらに同じくSpoutでUEのカメラ画をTouchDesignerに戻します。
これで、TouchDesigner側で映像のスイッチングをすることが出来ます。

UnrealEngine側のSpoutの出力には、Live-streaming Toolkitを利用します。
このプラグインはSpoutだけでなくNDIの入出力にも対応しており、便利です。

またTouchDesignerから飛ばすOSCをトリガーに、UEのマテリアルを操作します。
Actor BPにOSCの受信を定義し、Event DispatcherLevel BPのイベントを発火。
levelBP.png

最終的な画がこちら ↓↓↓
TDMovieOut.5.gif
今回はMixamoのBotを置いているだけですが、グリーンバックを用意すれば
UEのバーチャル空間に現実の人を合成することができ、XR表現を強化出来ます。

8. まとめ

今回はZoom Meeting SDKとTouchDesignerを組み合わせることで、
Zoomオペレーションシステムと映像演出をプロトタイピングしました。

これで課題解決。となれば良いのですが、現実はそういきません。
1台のPCに制御を集約するということは、それだけリスクも集中します。

リスク分散の観点から言えば、複数のPCと人を用意した方が良いかもしれません。

しかしドキュメントを見ていると様々な可能性が想像出来ますし、
今後もイベント業界でのZoomの利用シチュエーションは多くあるので、
検証を進めて現場に出せるようなシステムを開発したいと思いました。

5
5
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
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?