技術は私たちの力。技術は私たちの楽しみ。 Creative Developer BLOG 技術部ブログ
Technology is our strength. Technology is what we enjoy.

ブラウザベースのテレビ会議システムを作る

ブラウザベースのテレビ会議システムを作る
当社は、東京と札幌に事務所があるため、拠点間の打ち合わせにテレビ会議を行うことがよくあります。
普段はSkypeを利用しているのですが、たまに接続が不安定になり思うように会議が進められないことがあり、なんとかしなければいけないというのが、直近での課題としてありました。

そこで、隔週で各自持ち回りでやっている技術部勉強会のネタとして、前から気になっていたTwilioのプログラマブルVideoを使って、お手製のテレビ会議システムを作ってしまおう!ということでやってみました。

どんなしくみなのか

ブラウザまたは、iOS、Andoroidのアプリを通してテレビ会議を構築できるサービスで、Twilioが提供してくれる機能は主に以下の機能を提供してくれます。

1.認証と登録

テレビ会議を開始したい各クライアント端末をセキュアでスピーディーに認証してくれて、いつでもテレビ会議を開始できる状態してくれます。

2.通話の開始

会議を開始したい人がRoomと呼ばれる会議室のようなものを用意して参加者を募ることができます。

3.受信と通話の受け入れ

Roomに入ってきた人同士をピアツーピアで接続し、映像+音声または、音声のみで接続をし、会議を始められます。マルチパーティーに対応しており複数人での同時利用に対応しています。


要するに、例えばAさんとBさんがテレビ会議を始めたいとき、まずAさんが会議室「秘密の会議」を作ります。
Aさんは、Bさんに「秘密の会議」室を用意したことをメールや口頭で伝えます。
Bさんは、会議室名に「秘密の会議」と入力してその会議室に入室します。
すでにAさんは会議室で待機しているので、Bさんが入室したタイミングで、AさんとBさんがお互いの映像と音声が表示され、会議を始められるというしくみです。

Javascriptベースでの開発に用いられる基礎技術

一見シンプルな仕組みですが、その裏では最新のテクノロジーがふんだんに用いられ、数年前に同じことをしようとすると、ものすごい労力と技術力を結集させないと実現できない、高度な技術が詰め込まれています。
こんな素晴らしい仕組みをほんの数行のコードと僅かなコストで実装できるようにしてくれたTwilio社に感謝!

1.WebSocket

会議の開始や入室・退室のアクションをキャッチするためのしくみとして、WebSocket経由で Twilio に接続します。中間サーバーは不要で、通信はクラウドからエンドポイントへ直接処理されます。

2.イベント駆動型

会議室の中で起きる「〇〇さんが入室した」とか「退室した」という情報を「イベント」と言うかたちでTwilio SDKがJavaScriptで通知を出してくれるため、単純なコードで関数をより効果的に連鎖させることができます。

3.WebRTC 基盤への接続

WebRTCとは、プラグイン無しでウェブブラウザやアプリ間のボイスチャット、ビデオチャット等を実現可能にする、リアルタイムコミュニケーション用のAPIの定義です。
Twilioでは、このWebRTC基盤へ透過的に接続でき、低遅延で高品質なテレビ会議システムを構築することができるのです。

では早速作ってみよう!

まずは、Node.js環境でサンプルプログラムが動くか検証します。

Node.jsを3000番ポートで起動

ひとまず動きました。

ただ、一つ問題が発生。Chromeの場合、HTTPS環境じゃないとカメラやマイクへのアクセスを許可してくれなくて使えない!
FireFoxはひとまずOKだったので、動く環境は確認できましたが、FireFoxでしか使えないのはいまいち、、、
そこでNode.jsをSSL化しようと試行錯誤

Node.jsをHTTPSサーバに変更

Node.js番ソースコードを以下のようにHTTPSサーバ仕様に修正

require('dotenv').load();
var https = require('https');
var fs = require('fs');
var ssl_server_key = '/certs/key';
var ssl_server_crt = '/certs/certificate';
var ssl_server_ca = '/certs/ca-certs';

var path = require('path');
var AccessToken = require('twilio').AccessToken;
var VideoGrant = AccessToken.VideoGrant;
var express = require('express');
var randomUsername = require('./randos');

/* Create Express webapp*/
var app = express();
app.use(express.static(path.join(__dirname, 'public')));

var options = {
key: fs.readFileSync(ssl_server_key),
cert: fs.readFileSync(ssl_server_crt),
ca: fs.readFileSync(ssl_server_ca),
token:'/token'
};
app.get(options, function(request, response) {
var identity = randomUsername();

/* Create an access token which we will sign and return to the client,
containing the grant we just created*/
var token = new AccessToken(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_API_KEY,
process.env.TWILIO_API_SECRET
);

/* Assign the generated identity to the token*/
token.identity = identity;

/*grant the access token Twilio Video capabilities*/
var grant = new VideoGrant();
grant.configurationProfileSid = process.env.TWILIO_CONFIGURATION_SID;
token.addGrant(grant);

/* Serialize the token to a JWT string and include it in a JSON response*/
response.send({
identity: identity,
token: token.toJwt()
});
});

/* Create http server and run it*/
var server = https.createServer(app);
var port = process.env.PORT || 3000;
server.listen(port, function() {
console.log('Express ssl server running on *:' + port);
});

Node.js自体はSSLで立ち上がるようになりましたが、トークンの生成がうまく行かず、ちょっとハマりそうだったので、深追いせず一旦ここまで

使いやすいように改造しよう♪

Node.js環境で、カメラのプレビュー表示、Roomの作成、テレビ会議の開始、Roomからの離脱等一通りの動きは把握できたので、ここから少し改造。

【改造1】Node.jsで取得していたトークンをjQueryのajax通信で取得する

アクセス・トークンを生成するPHPを個別に用意

PHP、Node.js、Pyson等様々な言語用にサンプルプログラムを用意されているのですが、わかりやすいところでPHPを使います。

https://github.com/TwilioDevEd/video-quickstart-php
設定ファイルを編集
$ cp config.example.php config.php

設定ファイルの中身
$TWILIO_ACCOUNT_SID = 'アカウントSIDを入力';
$TWILIO_API_KEY = 'API keyを入力';
$TWILIO_API_SECRET = 'APIシークレットキーを入力';



Twilio HelperライブラリをComposer経由でインストールする
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"

$ php composer.phar install
説明ではphpのビルトインサーバを立ち上げて実行すると書いてますが、わざわざそんなことシなくてもApacheで十分なのでトークン作成の準備はここまででOK

jQueryのajax処理でトークンを受け取る

Node.jsでは、立ち上げ時にトークを生成するようになっていましたが、Room作成のタイミングのonClickイベントで生成するようにしました。
	$.ajax({
type: "GET",
url: '../php/token.php',
data : 'identity=' + identity + '&room=' + roomName,
success: function(ret){
var getToken = ret.replace(/\r?\n/g,"");

log("Joining room '" + roomName + "'...");
var connectOptions = { name: roomName, logLevel: 'debug' };
if (previewTracks) {
connectOptions.tracks = previewTracks;
}

Twilio.Video.connect(getToken, connectOptions).then(roomJoined, function(error) {
log('Could not connect to Twilio: ' + error.message);
});
},
error : function(ret){
log('Token作成エラー');
}
});
うまくいきましたね。
これでNode.js環境は特に不要になりました。

【改造2】参加者の名前をランダムで作成していたので、自分で名前を入力できるようにする

ここは特に難しいことはなく、名前を入力する項目を作って、jQueryで受け取るだけ

【改造3】カメラ画像を全画面表示できるようにする

実際のテレビ会議になると、相手のカメラ画像を全画面表示するのが一般的なスタイルだと思うので、サンプルになかった全画面表示ボタンを作りました。

全画面表示の実装

HTML側のボタン
<div id="full-screen_remote" class="full-screen" rel="remote-media">全画面</div>

jQueryのclickイベントでキャッチ
$(document).on('click', ".full-screen", function() {
var id = $(this).attr('rel');
var target = "#" + id + " video";
var video_id = id + '_video';
$(target).attr('id',video_id);
var elem = document.getElementById(video_id);
ElementRequestFullscreen(elem);
});

/* エレメントをフルスクリーン表示する関数*/
function ElementRequestFullscreen(element){
var list = [
"requestFullscreen",
"webkitRequestFullScreen",
"mozRequestFullScreen",
"msRequestFullscreen"
];
var i;
var num = list.length;
for(i=0;i < num;i++){
if(element[list[i]]){
element[list[i]]();
return true;
}
}
return false;
}
うまくいきました。
元の画面に戻るときは「ESC」ボタンで

改造後

サスケに乗せてみる

社内の打ち合わせで、サスケに集合!ができると便利かなと思い、サスケ内に移植しました。

サスケにテレビ会議機能を実装!

はい、うまくいきましたね。

まだまだやりたいことはたくさんありますが、「ブラウザベースのテレビ会議システムを作る」という目的は達成できたので、本日はここまで

ご清聴ありがとうございました。
記事一覧へ