ミーティングツールの映像を 360° プレーヤーで表示してみた
事の始まり
オンサイトとリモート混在での温度差
オンサイトとリモートが混在することが多い会議やイベントでは、どうしても双方の温度差が発生します。
それを解消すべく、360°カメラを製造しているメーカーでは、ここのところ多くの自社360°カメラ専用のツールを発表しています。
が、しかし、それらは同時利用できる人数の制限やランニングコストの増大といった問題に直面します。100人規模にでもなると尚更です。
それ以前に、すでに Google Meet や Microsoft Teams などのツールが導入されている場合、なかなか別のツールへの移行はしづらいというのが現状ではないでしょうか。
そこで、上記問題点を まるっと解決する方法はないか 検討してみることにしました。
実現方法検討
自前のミーティングツールを作る?
世の中には Google Meet Cloneのようなサンプルソースも多々あり、これで組んじゃえばなんて思ってみたり。
そしてカメラ映像を RTMPで送りつつ、それをWebRTCで配ればいいか...なんて考えてみたり。
が、どのプロトタイプを作ってみても、問題点が一つも解決できていないという結果に...
そもそも、自前で作ったミーティングツールをこの先 利用者がいる限り運用していくのは この上なく面倒です。
じゃあどうする?
では、どのように実装すべきが最適解でしょうか。
とりあえずの最低限の要件は以下となります。
- 既存のミーティングツールとは何とかして融合したい
- サーバ費用はあまりかけたくない
- ましてや、人数が増えることで 従量的に費用が増えることも避けたい
- ブラウザだけで何とかしたい
ゴール
これらを解決するであろう実装方法案はこちら。
- ミーティングツールは Google Meet または Microsoft Teams を利用
- ブラウザでアクセスできる
- サーバ不要
- すでに契約していれば 追加費用も不要(各クライアントの通信費のみ)
- 双方向の会話 / カメラ映像送信も標準装備
- ブラウザの拡張機能にて実装
- Google Chrome の拡張機能にて 360° プレーヤーを アドオン
- Google Chrome の拡張機能にて 360° プレーヤーを アドオン
実装
処理の流れ
それでは、以下の流れで動作するように実装してみましょう。
- Chrome で Meetに接続(これはブラウザとMeetの機能)
- カメラ映像が流れている videoタグを取得
- 取得したvideoタグの映像を アドオンした 360°プレーヤーに再描画
以下、ポイントを記載。
カメラ映像が流れている videoタグを取得
Google Meetで カメラ映像が流れてきている場合、videoタグが動的に生成されます。
<video class="[ClassID]" autoplay="" data-uid="240" style=""></video>
style は 映像元がカメラをオンオフした場合などに「display: none」が追記されることがあります。
それらを考慮した videoタグの検出はこちら。
videos = document.querySelectorAll('video[style=""], video[style="box-shadow: inherit;"], video[playsinline]');
※ 最後のplaysinlineは Teams用
検出した videoタグから 目的の360°カメラ映像を選択します。
※ 今回は、ユーザに選択してもらうように実装
また、生成されるvideoタグには id などは 設定されていないので、処理しやすいように プログラム内で必要な情報を付与してあげるとよいでしょう。
カメラ映像の映像主(表示名)を探す
映像主の名前は videoタグの数段上のdivタグ 配下に記載されています。日本語環境の場合、
<div class="[ClassID]" data-self-name="あなた">[映像主名]</div>
のような感じで生成されています。
※ data-self-name には全て "あなた"と記載されてるが、これは日本語環境の場合のみと思われる
なので、videoタグを起点に 親を辿りつつ 目的のタグを探します。大体 5回ぐらい上に上がれば見つかります(適当)。
例えばこんな感じ。
videos.forEach(function (video) {
child = video;
for (let i = 0; i < 5; j++) {
parent = child.parentNode;
users = parent.querySelector('div[data-self-name]');
if (users) {
// ユーザ名を取得して配列に入れてみる処理とか
break;
}
child = parent;
}
}
見つかるまでループするようにしてもよいでしょう。
なお、カメラ映像が増えたり減ったり(参加者がカメラをON/OFF)した時、対象の videoタグの個数が動的に変化します。
そのため、都度リストを更新する処理の実装が必要となります。
カメラ映像の取得
カメラ映像は 単純な videoタグのため、映像を単純に canvas に描き出します。
timerId = setInterval(async function(){
try {
canvas.width = canvasX = video.videoWidth;
canvas.height = canvasY = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvasX, canvasY);
} catch (e) {
// エラー処理
}
}, 1000/fps); // ex. fps = 30
360°プレーヤーの生成
canvas に描画できているので、 three.js & OrbitControls.js で 360°プレーヤーを作成します。
サンプルをお手本にすれば実装できます。
ここで、テクスチャの定義ですが、
texture = new THREE.VideoTexture( video );
のように videoから直接定義してもよいのですが、今回は、
texture = new THREE.CanvasTexture( canvas );
のように先に作成した canvas を経由して作成します。
理由は次項に記載の通り 動画にちょっとした加工を加えて表示したいと考えたためです。
ここまでで 360°プレーヤーの実装はほぼ完了です。
状況の可視化
顔認識・表情認識
この360°プレーヤーを使用するシーンは、主に会議やイベントを想定しています。
そのため、盛り上がりや雰囲気を数値化して、可視化しておきたいと思います。
そこで、前項の通り 折角 動画を一度 canvas に書き出しているので、これを用いてお試しとして人物の顔認識とその表情認識を実装します。
実装には、face-api を利用し、認識結果を 360°プレーヤーのための canvas 上に描き出します。
表情は face-api に合わせて以下のような絵文字を 推定度と併せて認識した顔付近にリアルタイム表示します。
- 😡 : angry
- 😩 : disgusted
- 😖 : fearful
- 😄 : happy
- 😐 : neutral
- 😭 : sad
- 😲 : surprised
数値化ができてしまえば、さらにグラフ化するなど様々な応用ができると思います。
完成
動作イメージはこちら。
埋め込み画像は こちら からお借りし、OBS の仮想カメラ経由で動画として配信しました。
360°カメラ映像そのまま(Meet標準)。
拡張機能ON。
左下に子ウィンドウを表示。元となるカメラ映像はグレーアウト。
この時点ですでに360°プレーヤーとして機能しています。
拡大表示もできます。
顔認識と表情認識。
構成例
構成例はこちら。
360°カメラは RICOH THETA Z1を使用しています。
THETAには、WebRTC Live映像を配信するためのアプリを開発・導入し、配信者はTHETAへhttp経由でアクセスします。この時のアクセス先は THETAの表示パネルに表示されます。
あとは、Chromeのタブ共有や OBSの仮想カメラなどで Meetに配信してあげるだけで、リアルタイムな360°映像を配信することができます。
なお、現在確認を受けていないTHETA Plugin は、ネットワークに接続された状態でのプラグイン起動がエラーとなります。
これを回避するには、THETA PLUG-IN STOREのプラグイン共有を申請する必要があります。
Live Streaming API を利用した THETA Plugin の開発ガイド | RICOH 4K Live Streaming
が、今回は面倒?なので、プラグインアプリに自動起動を仕込み、THETAの電源投入と共にプラグインを起動することで、誰でも電源を入れるだけで使用できるツールとして仕上げました。
あとがき
目指したゴールは概ねクリアできましたが、まだまだ課題もあります。
- カメラ映像のクオリティ
- Google Meetは カメラの最高画質設定が 720pのため、いまいち画質が悪い(今後の上限UPに期待)
- Microsoft Teamsの方が若干画質が良い
- 画面共有時の挙動
- Google Meetで 360°プレーヤー表示時に、誰かが画面共有を開始/停止すると描画が停止する(再度拡張機能のON/OFFで復旧)
- 現状 Meetの主催者機能で「参加者の画面共有」の許可をOFFにすることで回避
- Microsoft Teamsではこの問題はない
- Google Meetで 360°プレーヤー表示時に、誰かが画面共有を開始/停止すると描画が停止する(再度拡張機能のON/OFFで復旧)
いつの日か気が向いたら Chromeウェブストアに公開することにします。
Discussion