Janusを活用することでWebRTCに必要なサーバサイドの機能群を簡単に構築することができます。
そこで本記事では、WebRTCプラットフォームを運営するSkyWayが、Janusを使ってWebRTCのサーバー機能を簡単に構築する方法をサンプルコードや手順紹介しながら徹底解説!GitHubや便利なプラグインもご紹介します。WebRTCの実装経験が少なくても、スムーズに環境を構築できるようになりますので、ぜひ参考にしてみてください。
- WebRTCとは
- Janusとは
- JanusのGitHubをチェックする
- Janus WebRTC の主なプラグイン
- Janusを使ってWebRTCサーバーを構築してみよう
- サーバーサイド
- クライアントサイド
- 動作確認
- まとめ
代表的なビデオ通話プラットフォームとして、NTTコミュニケーションズが開発、運営する「SkyWay」があります。 「SkyWay」とは、ビデオ・音声通話をアプリケーションに簡単に実装できる国産SDKです。⇒概要資料をダウンロードする(無料)
WebRTCとは
WebRTC(Web Real-Time Communication)とは、ブラウザやアプリ間で音声・映像・データをリアルタイムに送受信できる技術です。P2P通信を基本とするため低遅延でスムーズな通信が可能です。
WebRTCでの通信を実現するためには何種類ものサーバーが必要となります。この記事ではそのようなサーバーをJanusを用いて簡単に構築する方法を解説します。
以下の記事にてWebRTCを詳しく解説しているので、ご参考ください。
Janusとは
Janusは、WebRTCを使ったオープンソースのメディアサーバーです。とても軽量かつ簡単に動作します。
機能としてはクライアント間の情報を交換する基本的なシグナリングサーバーの機能だけでなく、オンライン会議の映像や音声を効率的に中継する「SFU(Selective Forwarding Unit)」として動作したり、複数の音声をひとつにまとめて処理する「MCU(Multipoint Control Unit)」としても活用できます。これらの機能はプラグインを追加することで必要に応じて簡単にカスタマイズできます。
さらに、SIP、RTSP、RTMPなどの通信プロトコルとも連携できるため、他のシステムとの組み合わせも簡単です。APIを使って自由に制御できるので、オンライン会議、ライブ配信、防犯カメラ、IoTデバイスの通信など、さまざまな場面で利用できます。
公式サイトはこちら
https://janus.conf.meetecho.com/
JanusのGitHubをチェックする
Janus WebRTC Serverの公式のGitHubリポジトリには、ソースコード、ドキュメント、プラグイン情報、APIの詳細などが公開されています。サンプルコードや設定ファイルも充実しており、導入やカスタマイズの参考になります。
※GitHubは以下をご参照ください
https://github.com/meetecho/janus-gateway
Janus WebRTC の主なプラグイン
Janusには、様々な公式プラグインが用意されています。例えば以下のようなプラグインが存在します。
Echo Test plugin
WebRTC接続のテスト用プラグインです。送信した音声・映像をそのまま返し、エコーのように動作するため、通信環境やWebRTCの設定確認に便利です。
Video Call plugin
シンプルなビデオ通話プラグインです。シグナリングサーバーとして機能し、2 つの WebRTC ピアが Janus を介して互いに通話できるようにします。
Video Room plugin
SFU(Selective Forwarding Unit)として動作し、多人数のビデオ会議を可能にするプラグインです。各参加者の映像・音声を中継し、効率的な配信を実現します。
SIP plugin
SIPプロトコルと統合し、WebRTCと既存のVoIPシステムを接続可能にするプラグインです。IP電話との相互通話やPBXとの連携が容易になります。
Text Room plugin
WebRTCのデータチャンネルを利用し、リアルタイムのテキストチャットを実現するプラグインです。シンプルなメッセージ交換や通知システムの構築に適しています。
Streaming plugin
RTSPやRTMPなどのストリームをWebRTCに変換し、視聴専用のライブ配信を実現するプラグインです。WebRTCクライアントから遅延の少ないストリーミング再生が可能です。
それぞれの詳細やその他プラグインは以下のドキュメントをご参照ください。
Plugins documentation
https://janus.conf.meetecho.com/docs/pluginslist.html
今回は上記の中でも、シンプルなビデオ通話プラグインとして利用可能なVideo Call pluginを利用してWebRTCサーバーを構築してみます。
Janusを使ってWebRTCサーバーを構築してみよう
WebRTCでの基本的な通信の流れ
WebRTCでの通信ではシグナリングと呼ばれる過程が存在します。SDP(Session Description Protocol)と呼ばれる情報を2つのクライアントが交換し、通信の形式を決定して通信を開始します。このSDPには、送るメディアの形式、コーデックやIPアドレス、ポート番号、データ転送プロトコル、通信経路の候補といった情報が含まれています。
クライアントAがクライアントBに対して通信を繋げたい時、以下のようなフローで通信を開始します。
-
クライアントAがoffer SDP(自分の情報)を生成し、クライアントBに送信する
-
クライアントBは受け取ったoffer SDPを登録。Answer SDP(相手の情報と自分の情報を合わせて作った最適な形式)を生成し、クライアントAに送信する
-
クライアントAは受け取ったAnswer SDPを元に通信を開始する
一般的に、このプロセスを中継するためにシグナリングサーバーと呼ばれるサーバーを建てる必要があります。今回はJanusのサーバーを構築することによってその機能を実現します。
今回作るもの
JanusのVideoCall pluginでは、WebRTCを抽象化したregister
, call
, accept
といった概念で通話を管理します。サーバーはregister
のリクエストを送信されたユーザー名を管理します。クライアントは相手の名称を指定してcall
のリクエストを送信し、指定されたクライアントはaccept
のリクエストを返します。これによってWebRTCの通信開始までに必要なデータのやりとりを行います。その後サーバーを介したメディア通信を開始します。
上記のようにシグナリングサーバーやメディアの経由といった役割を持つサーバーとしてJanus WebRTC Serverを構築しました。また、構築したJanus WebRTC Serverを利用するための簡易的なクライアントアプリを作成しました。
サーバーサイド
サーバーはUbuntu 22で動かしました。
Janus WebRTC Serverを利用してサーバーを建てます。
導入
https://github.com/meetecho/janus-gateway
公式レポジトリのReadMeを参考に導入を行います。
各種必要なパッケージの導入を行います。
apt install libmicrohttpd-dev libjansson-dev \ libssl-dev libsofia-sip-ua-dev libglib2.0-dev \ libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \ libconfig-dev pkg-config libtool automake
必要に応じてビルド用のパッケージも導入します。
sudo apt install meson ninja-build
libnice
の導入を行う際は手動でコンパイルすることが推奨されているため、以下のコマンドからインストールを行います。
git clone https://gitlab.freedesktop.org/libnice/libnice cd libnice git checkout tags/0.1.22 meson --prefix=/usr build && ninja -C build && sudo ninja -C build install cd ../
libsrtp
はディストリビューションのバージョンが古い場合があるため、以下のコマンドで新しいバージョンをインストールします。
wget https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz tar xfv v2.2.0.tar.gz cd libsrtp-2.2.0 ./configure --prefix=/usr --enable-openssl make shared_library && sudo make install cd ../
依存関係のインストールが完了したら、以下のコマンドからjanus-gateway
のコードを取得し、コンパイルを行います。
git clone https://github.com/meetecho/janus-gateway.git cd janus-gateway git checkout tags/v1.3.1 sh autogen.sh ./configure --prefix=/opt/janus make make install
デフォルトの設定ファイルを以下のコマンドで設定します。
make configs
なお、ここの設定ファイルを操作することによって各種プラグインの利用有無等の細かい設定を行うことができます。
起動
以下のコマンドで起動を行います。
cd /opt/janus ./bin/janus
クライアントサイド
Web ブラウザ上で動作するJanus クライアントをNode.jsを用いて作成します。Janus公式のクライアントライブラリを利用します。
環境作成
任意のディレクトリにclient
ディレクトリを作成し、 そのディレクトリの中に以下の内容のpackage.jsonを配置します。
{ "name": "client", "version": "1.0.0", "scripts": { "build": "webpack" }, "dependencies": { "exports-loader": "^5.0.0", "janus-gateway": "^1.3.0", "webpack": "^5.98.0", "webpack-cli": "^6.0.1", "webrtc-adapter": "^9.0.1" } }
パッケージのインストールを行います。package.json
を配置したディレクトリ内で、以下のコマンドを実行してください。
npm i
webpackでのビルドの設定を行います。client
ディレクトリの中に以下の内容のwebpack.config.jsを配置します。
const path = require('path'); const webpack = require('webpack'); module.exports = { entry: './main.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, mode: 'development', plugins: [ new webpack.ProvidePlugin({ adapter: ['webrtc-adapter', 'default'] }) ], module: { rules: [ { test: require.resolve('janus-gateway'), loader: 'exports-loader', options: { exports: 'Janus', }, } ] } };
HTMLファイルの作成
表示するページの作成を行います。client
ディレクトリの中に以下の内容のindex.htmlを配置します。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Janus Video Call</title> </head> <body> <h1>Janus Video Call</h1> <video id="localVideo" autoplay muted></video> <video id="remoteVideo" autoplay></video> <audio id="remoteAudio" autoplay></audio> <button id="call">Call</button> <button id="hangup">Hang Up</button> <script src="dist/bundle.js"></script> </body> </html>
実装
ここからはコードの実装をしていきます。client
ディレクトリの中にmain.jsファイルを作成し、以下の内容を記載していきます。
Janus のインポートを行います。
import { Janus } from 'janus-gateway';
各種変数を定義します。
let janus = null; // janus instance let videocall = null; // videocall plugin handle let localStream = null; // local media stream
最初にJanusオブジェクトを初期化し、コールバックとしてインスタンスの作成処理を行います。ここで先ほど作成したJanusサーバーを指定します。
Janus.init({ debug: "all", callback: initializeJanus }); function initializeJanus() { janus = new Janus({ server: "http://<作成したJanusサーバーのIP>:8088/janus", success: attachPlugin, destroyed: () => window.location.reload() }); }
インスタンス生成後、プラグインのアタッチ処理を実行します。ここではプラグインとしてjanus.plugin.videocallを指定します。
function attachPlugin() { janus.attach({ plugin: "janus.plugin.videocall", success: pluginAttached, onmessage: handleMessage, onremotetrack: handleRemoteTrack, oncleanup: () => Janus.log(" ::: Got a cleanup notification :::") }); }
プラグインのアタッチ完了時の処理を記載します。各種エレメントの取得、自身の名称の取得、そしてJanusサーバーへのregister処理を行います。
なお、register処理を含めたJanusサーバーへの各種処理はvideocallというHandleを介すように設定します。
function pluginAttached(pluginHandle) { videocall = pluginHandle; Janus.log("Plugin attached! (" + videocall.getPlugin() + ", id=" + videocall.getId() + ")"); document.getElementById('call').addEventListener('click', doCall); document.getElementById('hangup').addEventListener('click', hangup); const myUsername = prompt("Enter your username:"); if (myUsername === null || myUsername === "") { alert("Please enter a username."); return; } // ローカル映像の表示 navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { localStream = stream; Janus.attachMediaStream(document.getElementById('localVideo'), stream); }) .catch(error => { console.error("Error accessing media devices.", error); }); let register = { request: "register", username: myUsername }; videocall.send({ message: register }); }
ボタンを押した際の処理を実装します。
発信者がCallボタンを押した時の通話を発信する処理を記載します。通話相手の名称を指定し、offer SDPを送信します。
Hang Upボタンを押した際は通話の終了を行います。
function doCall() { let username = prompt("Enter the username to call:"); if (username === null || username === "") { alert("Please enter a username to call."); return; } videocall.createOffer({ media: { video: true, audio: true, data: false }, stream: localStream, success: function(jsep) { Janus.debug("Got SDP!", jsep); let body = { request: "call", username: username }; videocall.send({ message: body, jsep: jsep }); }, error: function(error) { Janus.error("WebRTC error...", error); alert("WebRTC error... " + JSON.stringify(error)); } }); } function hangup() { videocall.hangup(); }
次にattachPlugin()で設定したイベント処理を実装していきます。
まずonmessageが発火した時の処理を実装します。
この時、incomingcallのメッセージの際は受信側が呼び出しを受けた場合で、acceptedのメッセージの際は発信側が受信側の返答を受けた場合のイベントとなります。
function handleMessage(msg, jsep) { Janus.debug(" ::: Got a message :::", msg); if (jsep !== undefined && jsep !== null) { if (msg.result && msg.result["event"] === "incomingcall") { handleIncomingCall(jsep); } else if (msg.result && msg.result["event"] === "accepted") { handleAcceptedCall(jsep); } } }
受信側が呼び出しを受けた場合の処理の実装を行います。videocall.createAnswerを実行し、作成してAnswerSDPとacceptしたという情報を発信側に返します。
function handleIncomingCall(jsep) { videocall.createAnswer({ jsep: jsep, media: { video: true, audio: true, data: false }, stream: localStream, success: function(jsep) { Janus.debug("Got SDP!", jsep); let body = { request: "accept" }; videocall.send({ message: body, jsep: jsep }); } }); }
発信側が受信側の返答を受けた場合の処理の実装を行います。videocall.handleRemoteJsep({ jsep: jsep })を実行することで、指定されたSDPでの通話を開始します。
function handleAcceptedCall(jsep) { videocall.handleRemoteJsep({ jsep: jsep }); }
remoteのtrackを取得した場合の処理を記載します。htmlエレメントにそれらのメディアをアタッチします。
function handleRemoteTrack(track, mid, added) { Janus.debug(" ::: Got a remote track event :::", track); if (added) { let stream = new MediaStream([track]); if (track.kind === "video") { Janus.attachMediaStream(document.getElementById('remoteVideo'), stream); } else if (track.kind === "audio") { Janus.attachMediaStream(document.getElementById('remoteAudio'), stream); } } }
これで実装が完了しました。
動作確認
ローカルに立てたJanus WebRTC Serverに対して接続できることを確認してみます。
(なお、リモートに作成したサーバーに接続する場合は別途ファイアウォールの設定等が必要になります)
以下のコマンドでビルドを行います。
npm run build
作成したhtmlファイルをブラウザから2つのウィンドウで開き、それぞれに名称を入力します。
片方のウィンドウでCallボタンを押し、もう片方の名称を入力するとそれぞれの映像・音声が表示されます。
これで疎通ができました。
まとめ
Janusは軽量なオープンソースのWebRTCメディアサーバーで、シグナリングサーバー、SFU、MCUなどの機能を提供します。この記事では、Janusを用いてWebRTCサーバーを構築し、Video Callプラグインを利用したビデオ通話の実装方法を解説しました。これ以外にも様々なプラグインがあり、SFUの利用やライブ配信機能、それ以外にも様々な機能を実現するサーバーとして利用することができます。ぜひ利用してみてください。
また、WebRTCの実装には高度な知識と膨大な開発工数が必要ですが、「SkyWay」を利用することで、スムーズかつ効率的に開発を進めることができるため、おすすめです。