NMOSコントローラーを自作してみた

以前のBLOGでNMOSの動きを追っかけてみましたが、今回はその続きというか、そこで得た知識を使ってNMOSコントローラーを自分で作ってみた話です。

以前のNMOS関連ブログ記事

なぜ作ったのか

きっかけは、ちょっとしたMoIPの検証をするたびに感じていた面倒くささでした。

IPゲートウェイでSDIをIP化して、波形モニタで受けて確認したい。やりたいことはそれだけなのですが、機器の設定画面から直接受信設定をしようとすると、毎回マルチキャストアドレスとソースIPを手で入力することになります。数字の羅列を1文字ずつ確認しながら打ち込む作業は、それ自体が目的ではないぶん、なかなかしんどいものがあります。

さらに厄介なのがST 2022-7です。冗長構成で検証しようとすると、amber面とblue面のそれぞれに入力が必要なのですが、機器によっては両方を同時に入れることができません。片面ずつ設定していくことになり、その間は当然きれいな冗長状態になっていないわけです。

もうひとつ、これも以前から思っていたことですが、SenderやReceiverを用途ごとにリネームして、人が見てすぐに把握できるコントローラーが欲しいというのがありました。NMOSが持っているlabelは IPT-10G2-SDI-1PT240401-TxVideo1 のように正確ではあるのですが、これを目で追いながら切り替えをするのは現実的ではありません。「カメラ1」「VTR1」と並んでいてくれれば、迷う余地はなくなります。

手元にちょっとしたNMOSコントローラーがあれば、検証がずいぶん楽になるのでは?

そう思って作ってみたのが今回の「NMOSコントローラー」です。

設計方針

作るにあたって決めたことは3つです。

1. ローカルで完結させつつ、操作はブラウザから

バックエンドはPython(FastAPI)、フロントエンドはReact(Vite / Tailwind)。データはCSVとJSONでローカルに保存し、外部DBは使いません。最終的にはPyInstallerで単一のexeにまとめて、Python環境のないPCにコピーしてダブルクリックすれば動くようにしました。検証用のPCに開発環境を作るところから始めたくなかったからです。

ただ、本体はexeで動かしつつ、操作はブラウザから http://127.0.0.1:8080 にアクセスする形にしています。デスクトップアプリとして作り込まなかったのは、Webにすることで画面を複数枚開けるからです。片方にルーティング画面、もう片方にリソース管理を出しておく、といった使い方ができるのは検証中にとても便利で、このアーキテクチャにしてよかったと思っています。

2. registryサーバーがなくても動く。あるなら使う

検証環境にいちいちregistryサーバーを立てるのは大げさです。そこで、登録したノードに対してIS-05で直接接続制御を行う作りを基本にしました。

ただ、これだけだと不十分だとも思いました。すでに運用中のシステムにはregistryサーバーがあるわけで、そこにはノードの情報がひととおり揃っています。あるものは使えたほうが便利ですから、RDSからデータを引っ張ってこられる機能も併せて実装しました。registryがなくても動くけれど、あればそこから拾ってくる。この両方を持たせたのが設計上のポイントです。

3. 現場で通じる名前で操作できるようにする

これが個人的には一番大事でした。目指したのは「カメラ1」「VTR1」といった、実際の運用で使っている名称でSenderとReceiverが並んでいる画面です。UUIDやlabelを見ながら切り替えるのではなく、用途を見ながら切り替えられること。この「名前をつけ直す」仕組みは最初から入れると決めていました。

機能紹介

画面は上部タブで4つに分かれていて、「ノード管理 → リソース管理 → パネル設定 → ルーティング」の順に進めば設定が完了する構成にしました。

1. ノード管理 — まず機器を見つける

最初のタブで制御対象のノードを登録します。ノードを見つける方法は3通り用意しました。

mDNS検出は、前回の記事で追っかけたあのマルチキャストDNSです。_nmos-node._tcp.local のクエリを投げて、応答してきた機器を一覧にします。同一サブネット内であればボタン一発で機器が出てきます。

RDS検出は、RDSのIPアドレスを入力してQuery APIから情報を取得する方式です。mDNSはマルチキャストなので同一L2セグメントに限られますが、別セグメントの機器はこちらで拾えます。検証環境でも制御ネットワークが分かれていることはよくあるので、両方必要でした。

検出結果は mDNS / RDS を1つの表にまとめて表示し、チェックを入れて「選択したノードを登録」で登録します。

そして3つめが手動登録です。

x-nmos のディレクトリを持っているIPアドレスとポート番号を手で入力すれば、mDNSやRDSからの情報がなくてもそのノードを扱えるようにしました。自動検出はもちろん便利なのですが、検証環境では機器がmDNSに応答しなかったり、そもそもRDSがいなかったりということが普通に起こります。「IPとポートさえわかっていれば必ず登録できる」という逃げ道を用意しておくことで、ツールとして手詰まりになる場面をなくしています。

登録済みノードの一覧では編集・削除ができます。また「同期」ボタンを押すと、選択したノードから devices / senders / receivers の情報を取得します。ここで取得した情報が、次のリソース管理画面の元データになります。

2. リソース管理 — 使用するリソースを選び、名前をつける

取得したSender / Receiverを、ノード・デバイスごとのアコーディオンで一覧表示します。

ここでやることは2つ。Aliasをつけることと、実際に使用するリソースを有効化することです。

写真では IPT-10G2-SDI-1PT240401-TxVideo1 というlabelに「AJAT-V」というAliasをつけています。今回は検証用の機材名をそのまま短くしていますが、実運用を想定するなら、ここを「カメラ1」「VTR1」といった用途の名前にしていくイメージです。元のlabelも小さく併記しているので、どのリソースなのかを見失うことはありません。

有効化は初期状態ですべてオフにしてあります。機器を1台つないだだけでSenderとReceiverが数多く並ぶことがありますが、ひとつの検証で実際に触るのはそのうちのごく一部です。使用するリソースだけを明示的に選んでおくほうが、操作時の取り違えを防げると考えました。

3. パネル設定 — 格子に並べる

有効化したリソースを、格子状のパネルにドラッグ&ドロップで配置します。列数・行数は自由に変えられます。

なぜ格子にしたかというと、慣れ親しんだルーターのコントロールパネルの見た目にしたかったからです。リストから選ぶUIでも機能的には同じですが、「左の列がReceiver、上のほうがビデオ、下がオーディオ」といった空間的な配置で覚えられるほうが、検証中の操作ミスが減ります。

ここではさらに、そのパネル上だけの表示名(panel_label)をつけられるようにしました。保存ボタンはなく自動保存で、いったん格子から外して再配置しても名前が復元されます。

なお、現状のパネルは1枚のみですが、将来的には複数枚のパネルを持てるように設計しています。用途ごとにパネルを使い分けられれば、検証の内容が変わるたびに配置し直す必要がなくなるはずです。

4. ルーティング — 同時TAKE

そして本題の切り替え画面です。パネル設定で作った格子がそのまま表示されます。

操作は単純で、Receiverのセルをクリックしてフォーカスし、続けてSenderのセルをクリックすると割り当てられます。このときフォーマットが一致するSenderしか選べないようにしてあります。video ↔ video、audio ↔ audio、data ↔ data。ビデオのReceiverにオーディオのSenderを当ててエラーになる、という不毛な時間を減らすためです。

そして複数のReceiver-Senderペアをまとめて作って、「TAKE」で一括実行します。映像と音声が別Senderに分かれている機器では、片方だけ切り替わった状態が発生すると確認になりません。「同時TAKE」はそのために入れた機能です。

もちろんST 2022-7の冗長構成もそのまま扱えます。IS-05のPATCHにはamber面・blue面それぞれのtransport_paramsが含まれますから、コントローラー側から投げてしまえば、機器の画面で片面ずつ入力する必要はありません。そもそもの発端がここだったので、これが動いたときはやはり嬉しかったです。

写真では、AJAR-V(Receiver)にAJAT-V(Sender)が割り当てられ、「接続先: AJAT-V」と表示されています。接続中のSender名は「パネル表示名 → Alias → label → UUID」の優先順位で表示するようにしていて、名前をつけていないものも必ず何かしら表示されます。

TAKEの挙動でひとつこだわったのが、実行後も割り当てとフォーカスを保持するようにしたことです。当初はTAKEするたびにクリアしていたのですが、実際に検証で使ってみると「受け側は固定したまま、送り側だけを次々切り替えてモニタで確認する」という使い方が圧倒的に多く、毎回Receiverから選び直すのが煩わしかったため、途中で仕様を変更しました。

失敗したときはエラー履歴に対象名・処理段階・エラー種別・内容を残すようにしています。直近50件を保持し、消去するまで残ります。検証中は後から見返せることが重要なので、これは必須の機能でした。

実装で苦労したところ

ひとつ挙げるなら、TAKEの応答速度です。

初期の実装では、TAKEのたびにAPIの版数を確認するリクエストを投げていました。当然そのぶん切り替えまでに時間がかかります。そこで、同期を行ったタイミングで検出した版数をキャッシュして共有するようにし、TAKE時には版数確認のアクセスをしないよう変更しました。加えてTCPのkeep-aliveを効かせて接続を再利用し、切り替え後のActive状態の検証も、間隔を詰めて1回目で一致すれば即確定するようにしています。

切り替えの体感速度は、検証ツールとしての使い勝手を大きく左右します。ボタンを押してから映像が変わるまでの待ち時間が長いと、それだけで確認作業のテンポが崩れてしまうので、ここは粘って調整しました。

生成AIで作りました

最後に正直に書いておくと、このツールのコードはほぼ生成AIによるコーディングで書いています。開発環境として使ったのはAWSのKiroです。

AIの力を借りたとはいえ自作でやっているからこそよかったのは、検証で使っていて「ここにこの機能が欲しい」と思ったときに、すぐ作り直せることです。TAKE後に割り当てを保持する仕様に変えたのもまさにそれで、使いながら直す、直したものをまた使う、というサイクルを短く回せるのは市販のツールにはない利点でした。

将来的には、実運用のRDSに接続して、手元のモニタだけをちょっと制御する、といった使い方ができるところまで持っていきたいと考えています。大げさなシステムの代わりになるものではありませんが、そういうちょっとした場面で役に立つツールになればと思っています。

まとめ

いかがでしょうか?

NMOSの動きを追っかけた2本の記事は「理解する」ための検証でしたが、今回は「作る」ところまでやってみました。実際に自分で実装してみると、規格を読んだだけ・パケットを見ただけでは気づかなかった細かい部分がいろいろと見えてきて、理解がもう一段深まったように思います。

この記事を最後までお読みいただきありがとうございます。少しでもみなさまの知識の役にたてれば幸いです。

 

Previous Post