WebRTCこんにちは。Tomofilesです。前回、開発中のSkysign-Cloudというドローンクラウドサービスの紹介をしました。tomofiles.hatenablog.comその中で、次に実装したい機能として、映像配信機能を挙げました。映像配信機能を実装するにあたり、現在、色々と調査・検証を進めているところですが、いったんの見通しが立ったので、ここでまとめておくことにします。大きな価値を生むテクノロジーとなる可能性があります。現時点でも、映像配信機能を提供している既存サービスも存在していますが、私もそれらに同調して実装してみようと思います。今回は、ある要件を満たすために、なるべく低遅延で映像をクラウド経由で複数人で視聴できるようにしたい、というニーズが発生したことを想定して、色々考えていきたいと思います。笑)それでは、いきましょう。ちなみに、今回も1万字を越えています。映像配信方式について考える映像配信といっても、実現方式は色々選択肢があります。調べてみると、映像配信(というか、動画配信)といっても3種類のパターンがあるそうです。静的動画コンテンツ配信はそのままですね。ライブ動画配信は生放送系サービスです。ニコ生とかSHOWROOMとか、1対多で、基本的に1→多の一方通行の配信方式のことを言います。リアルタイム動画配信は、テレビ会議システムやビデオチャットなどのサービスですね。Skypeとかがそうなんでしょうか。この辺の詳しい話は、以下のサイトがわかりやすいです。GitHubさて、ドローンの映像配信を考えたときにも、これらの3パターンは以下のように利用シーンが考えられます。静的動画コンテンツ配信イメージとしては、ミッションフライト中の撮影動作と、フライト後の映像配信が厳密に分かれている場合ですね。今すぐに映像データが必要ない場合などは、無理にリアルタイム配信を目指す必要はありません。ドローンが帰還してから、映像データをクラウドにアップロードすれば済みます。ライブ動画配信ライブ動画配信は、ある程度の遅延が許容される場合に採用されます。ネット動画の生放送配信は、ある程度のリアルタイム性さえ実現できていれば、30秒遅れていても視聴者は気になりません。(あまりに遅いとSNSに先を越されて、視聴者が違和感を感じてしまうかもしれないですが)遠隔ドローン監視の場合、正直このレベルで用が足りる可能性はあります。農業におけるほ場の観測データなどは、ある程度リアルタイムで視聴できればいいので、ライブ動画配信方式で十分でしょう。(逆にこれは、静的動画コンテンツ配信方式だと、やり直しの場合最初からとなってしまうので、経過を見ながらフライトさせたいですね)リアルタイム動画配信リアルタイム動画配信方式は、ドローンとコミュニケーションを取りたい場合などは有効です。配信映像を見ながらフライトの指示を出したい(ホーム帰還コマンド、撮影コマンドなど)場合などは、あまりに遅延があると難しいですよね。それ以外にも、例えば災害時に巡回飛行しながら、逃げ遅れた市民にドローン搭載のスピーカーから、遠隔で声を掛けたい場合などは、映像と時間的にリンクしていないと実現できません。(現在、世界中で新型肺炎が流行していますが、中心地の中国では、ドローンで市民にマスク着用を警告してまわってる、なんて噂も出てきてますね)この記事の導入でざっくりと要件を示したのは、映像配信方式として、これらの3パターンが挙げられるため、どれを選択すべきかという根拠が必要なためでした。今回は、なるべくリアルタイムに近づけることで、ドローンとコミュニケーションをするシーンを想定して、実現を目指してみたいと思います。マルチメディアの通信プロトコルさて、ウェブを前提として、現在どのような方式で、映像データ(マルチメディアデータ)が配信されているでしょうか。ストリーミング配信に絞って少し調べた限りでは、以下の選択肢があるようです。HLSMPEG-DASHRTMPそして、配信という枠とは少し違う位置付けで、WebRTCとなるようです。ウェブを前提とするなら、HTTPベースのHLS、MPEG-DASHが選択肢として挙がるでしょう。動画をHTTPで扱えるコンテンツとして配信できるため、CDNと連携することで広域に対して大規模なストリーミング配信が実現できます。HTTPなのでPULL型の仕組みとなり、遅延はある程度発生するようです。1〜2秒までは抑えることができるそうですが、それなりにチューニングを施さないといけないのかもしれません。前述の3つのパターンのうち、静的動画コンテンツ配信と、ライブ動画配信であれば、このプロトコルが採用となるでしょう。Flashで使用されていた通信プロトコルで、HTTPではなくTCPの上に構築されています。PUSH型なので、仕組み的には遅延が発生しにくいプロトコルのようですが、ウェブベースではないのでブラウザで直接扱うことはできず、Flashを導入するか、クライアントアプリケーションが必要となります。Flashが今年(2020年)に終了宣言を出されているので、このプロトコルも徐々に勢いは減っていくのでしょうか?以前、インターネットラジオ"Radiko"でRTMPの音声データをストリーミングしたことありますが、ffmpegなどのツールと組み合わせても、とても扱いやすくて良かった印象があります。前述の3つのパターンでは、RTMPはリアルタイム動画配信の場合に、相性がいいでしょう。今回、私は、これらの通信プロトコルの採用も検討しましたが、結局WebRTCを使用してみることにしました。Drones(IoD)の私の中のイメージは、ウェブベースでドローンとコミュニケーションして双方向にアプローチできる必要がある、というものです。できるところまでドローンとリアルタイムで通信する方法を目指しつつ、必要に応じて不要な枝葉を落としていけばいいかなと考えています。WebRTCとはWebRTCは、厳密には通信プロトコルではありません。新しい技術というより、既存の標準技術を一つにまとめて、ブラウザから手軽にリアルタイム通信ができるように整備された、いわばセットものです。難しい話も多く、全体像を理解しにくい技術ですが、以下のサイトのシリーズ記事が分かりやすかったので、貼っておきます。2016年の記事ですが、現在の仕様と大きくズレるところはないと思います。html5experts.jpあとは、以下のサイトとかも、全体感がまとまってて、わかりやすいですね。Peer(P2P)を実現する技術なのですが、P2Pを実現するには多くの障害があり、それを様々な方法で、どうにか解決しようというのが、この技術のキモです。接続先のホスト内がFirewall的に働くネットワーク構成にしていると、外から見てホスト内が隠蔽されているため、直接接続することができないという感じです。宛先(IPアドレス)が分かれば、接続先Peerとの通信経路を探すことができます。自分のローカルIPアドレス自分のグローバルIPアドレス相手のグローバルIPアドレス相手のローカルIPアドレスとなります。これらの情報を集めて通信経路を探すために、「STUNサーバ」と「TURNサーバ」という2つのサーバに、アクセスして情報を集めます。STUNサーバは、自分のグローバルIPアドレスを取得できます。相手も同じく、自分のグローバルIPアドレスをSTUNサーバに問い合わせます。ここで集めた接続情報から、実際に通信経路を探せなかった場合に、TURNサーバ経由でアクセスすることで、通信経路の候補を探します。例のFirewall的なネットワーク構成とかですね。TURNサーバは、こういうときの、最終手段の通信経路を提供するサーバということです。Candidateといいます。この情報を「シグナリングサーバ」というサーバで、P2P接続をする前に、相手と交換して、接続の準備を行います。相手との通信経路の合意メディアコーデックの合意などを含みます。このSDPの交換をシグナリングといいます。これ以外にも証明書情報の交換なども含むそうです。この一連の手続きを行うことで、やっとP2P通信を開始することができるのです。超ザックリとした解説となりましたが、図付きのわかりやすい説明をしているQiitaの記事があったので、貼っておきます。qiita.comあとは、以下も手続きがわかりやすいです。https://blog.wnotes.net/posts/webrtc-beginning/ドローンの映像配信に適用する今回、WebRTCをドローンの映像配信に適用するにあたり、以下のことが実現できるか検証しました。単一ドローンから、複数クライアントに対する映像配信ドローンのコンパニオンコンピュータ上で動く、ブラウザレスなWebRTCクライアントの構築ドローンとクライアントが別のホストに属する場合の、NATを越えたPeer間の映像配信これらのトピックから、Skysign-Cloudへのリアルタイム映像配信機能の実装に向けて、以下の概念図のような基礎モデルを構築し、実現性の確認を行いました。ソースコードは例のごとく、Githubに公開しています。github.com今回の基礎モデル構築に向けて、以下のGo言語のOSSのWebRTCライブラリを使用しました。この基礎モデルは、以下のOSSのサンプルソースやサンプルアプリケーションを組み合わせて作成しています。github.com早速、動かしながら、検証結果を見ていきましょう。クラウド側の構築まずは、クラウド側の構築です。クラウド側で実現したいことは、以下のトピックです。STUN/TURNサーバの構築シグナリングサーバの構築1対多の映像配信(SFU)の実現STUN/TURNサーバの構築STUN/TURNサーバは、今回pion/turnのサンプルに含まれている、turn-server-simpleを使用します。github.compion/turn自体は、APIライブラリなのですが、それを簡単に試すことができるサンプルとして、simple版を提供しています。これを使えば、認証情報などは固定となりますが(起動時に引数で渡す)、挙動の確認などが行えます。turnとしか表記されていないですが、STUNの機能も持っていますので、これ一つで大丈夫です。このturn-server-simpleを、クラウド上で実行してみましょう。Platform(GCP)を使用します。GCEインスタンスを一つ作成して、諸々の初期設定を行ってください。ここで、作成したGCEのパブリックIPアドレスを確認しておきます。次に、turn-server-simpleのバイナリを取得します。以下から、「turn_2.0.1_Linux_x86_64.tar.gz」をダウンロードして解凍します。この記事執筆時は、最新バージョンが2.0.1でした。GitHubあとは、実行するだけです。ユーザ名とパスワードを=で繋ぎますこれだけです。ここで設定したユーザ名とパスワード、および、GCEのパブリックIPアドレスは、あとで使うので、控えておいてください。シグナリングサーバの構築シグナリングサーバは、私が作成したプロジェクトのものを使用します。Go言語で書かれているので、Goのコンパイラを用意してください。シグナリングサーバ用に、もう一台GCEのインスタンスを新しく作成します。以下の手順で、ビルド・実行します。<ユーザ名>=<パスワード>なお、シグナリングサーバは、Websocketでクライアントから接続されますが、セキュア通信でないといけないので、証明書の登録をしないといけません。さきほどシグナリングの説明のときに証明書の話が少し出てきましたが、セキュア通信であることがWebRTCの仕様に含まれているのです。GCPでセキュア通信をするには、以下の作業が必要です。証明書の取得L7ロードバランサの設定DNSに登録あまり細かくは書かないですが、私は以下のように、これらを用意しました。Domainsで購入しました。今回用ではなく、そもそも私個人のドメインを取得しておきたいと思ったので、過去に買ったやつを今回も利用します。証明書はGCPのロードバランサを設定する際に、フロントエンドの設定の項目で作成できます。独自の証明書等の指定がなければ、Google管理の証明書を数クリックで作成できます。L7ロードバランサの設定GCPの負荷分散で、HTTP(L7)ロードバランサを作成します。バックエンドをシグナリングサーバのインスタンスに指定して、フロントエンドをHTTPSで設定して、証明書を作成し、登録します。DNSに、取得したドメインでゾーンを作成します。サブドメインを切るなどして、レコードセットを追加してください。このとき、上で作成したロードバランサのIPアドレスを指定します。最後に、ゾーンのネームサーバを、DNSに設定します。Domainsのネームサーバの欄に入力して保存します。DNSに反映されるまで、数時間程度を見ておきましょう。URLにアクセスすると、以下のような画面が表示されます。このシグナリングサーバは、pionの「sfu-ws」というサンプルアプリケーションがベースになっているので、このサンプルと同じ機能が試せます。試しに、複数端末で、PublishとSubscribeをして、WebRTCの映像配信を試してみてください。1対多の映像配信(SFU)の実現1機のドローンのカメラから、複数のクライアントに映像を配信するためには、シンプルなWebRTCだと問題があります。WebRTCはP2Pでメディアデータをやりとりするので、Peerが複数になってくると、配信側に負荷がかかるようになります。Unit)というモデルを利用することで、この問題を解決できます。SFUは厳密にはP2Pではなく、サーバ/クライアント構成で、擬似的なP2Pを実現します。イメージとしては、通常のP2Pがクライアント⇔クライアントだとしたら、SFUは間に中継サーバを挟んで、クライアント⇔SFUサーバ⇔クライアントという感じに構成されます。今回の基礎モデルのイメージ図の中の、緑色の線がP2P接続(ICE)を表すのですが、SFUサーバからも伸びていますね。これは、このSFUを意味しています。SFUに関しては、以下のサイトで詳しく説明されています。GitHub前述したとおり、今回のサンプルはpionの「sfu-ws」というサンプルアプリケーションがベースになっています。荒削りなサンプルで、一回SFUの接続を行ったら、プロセスを再起動しないといけない仕様ですが、今回は理論のお試しなので、そのまま使用しています。続いて、エッジ側の構築を行っていきます。SFUのサンプルアプリケーションは、ブラウザ同士のP2P接続であれば、これだけで動作を試すことができます。エッジ側に構築するのは、ブラウザの代わりに、ドローン上のコンパニオンコンピュータからP2P接続を行うモジュールです。配信側のPeerに、ドローンがなりすますイメージですね。エッジ側のトピックは、以下のとおりです。カメラデバイスにアクセスWebRTCクライアントの構築カメラデバイスにアクセスWebRTCの標準仕様には、カメラデバイスのアクセスに関する決まりごとも含まれているのですが、あくまでブラウザから利用する場合を想定したものです。ブラウザレスでP2P接続を行う場合、何らかの方法でカメラデバイスにアクセスし、メディアデータのストリームを取得して、WebRTCのメディアチャネルに流し込む必要があります。pionのサンプルソースの中には、GStreamerでカメラデバイスに接続して、メディアストリームを取得するサンプルが提供されています。今回は、その仕組みを応用して、エッジ側のアプリケーションを作成しました。github.comただ、今回私はエッジ側では、以下のことをこだわっていたのですが、このサンプルをそのまま使うには、少し都合が悪いことがいくつかありました。ピュアGoで実装するビルド環境でクロスコンパイルして、RPiに持ち込むサンプルソースでは、GStreamerをcgoライブラリでインポートして利用していました。これが非常に厄介で、クロスコンパイルでARM7向けにビルドしようとすると、ビルドエラーとなってうまくいかないのです。調べて、結局解決は諦めたのですが、cgoのクロスコンパイラをダウンロードして、参照させるビルド環境にRPiのSDカードをマウントして、pkg-configを参照させるなどがネット検索すると出てきて、少し敷居が高い+環境依存し始めていて、途中でストップしました。私は、Go言語の良いところが、環境に依存せず、クロスコンパイルすれば、どこでも動くものが作れることだと思っています。成果物がシングルバイナリなので、ランタイムも不要で、環境を汚しません。この特徴を考えたとき、無理に一つの実行体に固める必要はないと判断しました。いうことで、諸々を考慮して、GStreamerは直接実行して、カメラデバイスから取得したメディアストリームをRTPで、RPi内のポートに公開することにしました。以下のコマンドをRPiで実行し、GStreamerを起動しましょう。&上の概念図の数字が書いた青い箱はポート番号を示していますが、videoストリームは8888ポート、audioストリームは8889ポートに公開するようにします。今回のドローンの映像配信では、audioはなくても良いのですが、WebRTCとしては揃っていないといけないので(videoだけ、とかできるのでしょうか?)、audiotestsrcを用いて空のストリームを作成しています。カメラデバイスとRPi4は、以下の写真の様にUSBで接続しています。エッジ上に載せるWebRTCクライアントアプリケーションを、コンパニオンコンピュータ(RPi4)上に構築しましょう。先程ダウンロードした私のサンプル「webrtc_base_model」から、webrtc-edge-webcamを使用します。<ユーザ名>=<パスワード>このwebrtc-edge-webcamを実行するタイミングは、webrtc-cloud-sfuを起動してから一番最初でないといけません。webrtc-cloud-sfuがPublisherから受け入れる仕様となっているためです。以下の順番で実行していくと、映像配信が試せるかと思います。turn-server-simpleを起動webrtc-cloud-sfuを起動webrtc-edge-webcamを起動クライアントがブラウザからアクセスしてSubscribeボタン押下これで、配信されてきた映像が視聴できます。以降、複数のクライアントがSubscribeしても、配信されている映像を、複数同時に見ることができます。まとめ今回も、とても長文となってしまいましたが、ドローンの映像配信の基礎モデルについて、考察してきました。ここまで、ドローンの映像配信にWebRTCを利用できないか検証してきましたが、実際に飛行させてないので、有効性の判断などはできていません。RPi上にWebRTCの映像配信モジュールの構築ができて、SFUによる複数クライアントの視聴が確認できたので、実現性はあるのではと考えています。あとは、Skysign-CloudのEdgeに適用したときの、リソース使用量とかネットワーク帯域の確認とか、RPi4でどこまで実用に耐えられるのか、確認してみたいですね。一応、映像の遅延は、体感的に1秒未満という感じです。GStreamerにより映像のサイズが320×240に縮小されているので、この辺りを調整していくと、もっと遅延したりする可能性はあります。チューニングでどこまでできるのか、これから色々試してみたいですね。Skysign-Cloudへの映像配信機能の実装を、目指してみましょうか。ここまで読んでくださって、ありがとうございます。beta版完成〜こんにちは。Tomofilesです。機体ビルド〜こんにちは。最近仕事が少し忙しくて、自分の時間が取りにくい...2019-11-17ドローンと向き合った1Qの振り返りこんにちは。前回のドローンレースボランティアの参加レポが、...2019-11-08ドローンレースにボランティアとして参加しました。#DTRC201911月1〜2日に東京モーターショーのイベントとして行われた、FAI...2019-11-04MAVSDKでSITLのドローンからテレメトリーを取得してCesiumJSにアニメーション表示してみ...こんにちは。)はてなブログをはじめよう!Tomofilesさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。