Twilioブログ

Twilio Videoアプリケーションに画面共有機能を追加してみましょう

kana.aoki

header_twilio_video-768x307.png

直近のブログでは、ChromeFirefox でユーザーの画面をキャプチャする方法を紹介しました。今回は、これをリアルタイムビデオチャットの一部として実行できるよう、ビデオチャットアプリケーションに統合してみます。

これから作るもの

「Twilio Video クイックスタートアプリケーション」をベースに、ここへ画面共有機能を追加してみます。完成時のアプリケーションは、アプリケーション間のブラウザコールに加え、お互いの画面共有ができるようになります。ビデオチャットの中で画面共有が済んだら、再び Firefox や Chrome の制御に戻します。

Successfully sharing a screen from one video chat to another and then back again using Firefox and Chrome

必要なもの

このアプリケーションを立ち上げるには、以下のものが必要です。

はじめるにあたり、まずGitHubのレポジトリのクーロンを作成し、"the building-screen-sharing branch" をチェックアウトしてください。

git clone -b building-screen-sharing https://github.com/philnash/screen-capture.git
cd screen-capture

このレポジトリは "the quickstart repo" とは少し違い、直近2回のブログで作ったものが含まれており、Chromeでスクリーンショットを取るために必要なエクステンションも入っています。詳しくは、Screen capture in Google Chrome の記事を参照してください。

ビデオチャットアプリを準備するために、video-chat ディレクトリ に変更を加え、依存性のあるアプリケーションをインストールします。

cd video-chat
npm install

上記の作業を終えたら、Twilio Video に接続するためにいくつかのクレデンシャルを加えましょう。最初にファイル .env.example .env としてコピーし、.env を開いて詳細を記述していきます。ご利用の Account SID と APIキー・その Secret が必要になるので、コンソール(管理画面) で確認してください。

そして実行します:

npm start

ブラウザで http://localhost:3000/ を開きます。ユーザー名とルーム名を入力すれば、ルームに接続されるはずです。別ブラウザ(別タブ) で同じように繰り返せば、それら同士でビデオチャットが成立します。ここまでが上手くいったら、いよいよ画面共有機能を追加してみましょう。

画面共有機能の追加

画面共有機能の追加まであと一歩です。最初に、ブラウザがスクリーンキャプチャに対応しているかを確認してください。これがないと始まりません。ユーザーが画面共有をする際には、まずそのブラウザが何なのかを判定し、そして、ストリームを取得します。これは以前のブログ記事2つに載せてあるコードを使います。

ブラウザサポートのチェック

私は通常、プログレッシブエンハンスメント(閲覧環境の多様化)に対応するよう、Java Scriptベースでこのような検知ロジックを書くのですが、これまでのブログ記事をご覧いただいて分かる通り、スクリーンキャプチャ自体がまだ標準化されていないのです。なので・・・ 仕方ない、美意識に欠けるコードを書かざるを得ません。

Firefoxにおけるスクリーンキャプチャのサポートは、mediaDevice API mediaSource の制約がベースになります。以下でそれをテストできます

!!navigator.mediaDevices.getSupportedConstraints().mediaSource

これはこれでよいテストなのですが、残念なことに全てを賄えるわけではないです。Firefox は、mediaSource サポートについてバージョン33 まで遡るとしているものの、このサポートは信頼できるサイトセットへホワイトリストとして登録されています。ですので、そのFirefox のバージョンが 52 以上なのかをチェックする必要があるのです。この情報は、User Agent を正規表現でマッチさせて読み取ることができます。

var matchData = navigator.userAgent.match(/Firefox/(d )/);
var firefoxVersion = 0;
if (matchData && matchData[1]) {
  firefoxVersion = parseInt(matchData[1], 10);
}

video-chat/src/index.js を開いて、文頭に Firefox でテストを行うためのすべての機能を加える

!!navigator.mediaDevices.getSupportedConstraints().mediaSource

これはこれでよいテストなのですが、残念なことに全てを賄えるわけではないです。Firefox は、mediaSource サポートについてバージョン33 まで遡るとしているものの、このサポートは信頼できるサイトセットへホワイトリストとして登録されています。ですので、そのFirefox のバージョンが 52 以上なのかをチェックする必要があるのです。この情報は、User Agent を正規表現でマッチさせて読み取ることができます。

var matchData = navigator.userAgent.match(/Firefox/(d )/);
var firefoxVersion = 0;
if (matchData && matchData[1]) {
  firefoxVersion = parseInt(matchData[1], 10);
}

Chromeエクステンションがインストールしてあるかを検知する方法はいくつかありますが、このブログでは範囲を超えてしまうので触れません。この先の記事で触れるのでチェックしてみてください。今回の記事は、Chromeウィンドー内にあるオブジェクトからコールを発信できるかを試みるのが目的です。

  return mediaSourceSupport && firefoxVersion >= 52;
}
 
function isChrome() {
  return 'chrome' in window;
}

それと、スクリーンキャプチャが可能かどうかを判断する方法も探す必要があります。

 return 'chrome' in window;
}
 
function canScreenShare() {
  return isFirefox() || isChrome();
}

スクリーンをキャプチャする

これまで触れた機能と、過去2つの記事で紹介したノウハウを使い、サポートするブラウザでスクリーンキャプチャができるようになりました。このパートでは、みなさんの Chromeエクステンション のIDが必要になります。video-chat/src/index.js に以下のコードを加え、YOUR_EXTENSION_ID プレイスホルダーを、ご自身のIDに置き換えてください。

 function getUserScreen() {
  var extensionId = 'YOUR_EXTENSION_ID';
  if (!canScreenShare()) {
    return;
  }
  if (isChrome()) {
    return new Promise((resolve, reject) => {
      const request = {
        sources: ['screen']
      };
      chrome.runtime.sendMessage(extensionId, request, response => {
        if (response && response.type === 'success') {
          resolve({ streamId: response.streamId });
        } else {
          reject(new Error('Could not get stream'));
        }
      });
    }).then(response => {
      return navigator.mediaDevices.getUserMedia({
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: response.streamId
          }
        }
      });
    });
  } else if (isFirefox()) {
    return navigator.mediaDevices.getUserMedia({
      video: {
        mediaSource: 'screen'
      }
    });
  }
}

これでユーザーが画面を共有したい時に、この機能が使えるようになりました。 全てを通してみましょう 最後のパートです。インターフェイスをこれまでの機能につなげてみます。ルームに入った時に有効化される隠れたボタンがあります。 roomJoinedfunction には既に見えるボタンと見えないボタますが、以下を加えます。

全てを通してみましょう

最後のパートです。インターフェイスをこれまでの機能につなげてみます。ルームに入った時に有効化される隠れたボタンがあります。 roomJoinedfunction には既に見えるボタンと見えないボタンが含まれていますが、以下を加えます。

document.getElementById('button-join').style.display = 'none';
  document.getElementById('button-leave').style.display = 'inline';
  if (canScreenShare()) {
    document.getElementById('button-share-screen').style.display = 'inline';
  }

ルームを退出するためのボタンのイベントハンドラーの下へ、画面共有・画面共有解除のハンドラーを作成します。

document.getElementById('button-leave').onclick = function() {
      log('Leaving room...');
      activeRoom.disconnect();
    };
    
    document.getElementById('button-share-screen').onclick = function() {
    };
 
    document.getElementById('button-unshare-screen').onclick = function() {
    };

ここで、ユーザー画面のメディアストリームとビデオトラックを取得するための getUserScreen ファンクションを画面共有ボタンのハンドラーの中に組み入れます。これでルーム内の localParticipant のトラックを発行します。全て上手くいけば、他のボタンも変えていきましょう。

document.getElementById('button-share-screen').onclick = function() {
      getUserScreen().then(function(stream) {
        var screenTrack = stream.getVideoTracks()[0];
        activeRoom.localParticipant.publishTrack(screenTrack);
        document.getElementById('button-share-screen').style.display = 'none';
        document.getElementById('button-unshare-screen').style.display = 'inline';
      });
    };

画面共有解除ボタンについては、トラックの発行を取りやめるようにしたいです。
このためには、画面トラックのリファレンスが必要になります。
ファイルの先頭で、新規の screenTrackvariable を宣言します。

var activeRoom;
var previewTracks;
var identity;
var roomName;
var screenTrack;

getUserScreen へのコールバックするときは、var キーワードを取り除いてください。

document.getElementById('button-share-screen').onclick = function() {
       getUserScreen().then(function(stream) {
        screenTrack = stream.getVideoTracks()[0];
        activeRoom.localParticipant.publishTrack(screenTrack);
        document.getElementById('button-share-screen').style.display = 'none';
        document.getElementById('button-unshare-screen').style.display = 'inline';
      });
    };

画面共有解除ボタンのハンドラーについては、screenTrackvariable を使って localPaticipant からのトラックを発行を取りやめます。

document.getElementById('button-unshare-screen').onclick = function() {
      activeRoom.localParticipant.unpublishTrack(screenTrack);
      screenTrack = null;
      document.getElementById('button-share-screen').style.display = 'inline';
      document.getElementById('button-unshare-screen').style.display = 'none';
    };

これで必要なコードが全てそろいました!

画面の共有

アプリケーションを再度走らせます。もし走っていなければ、npm start で実行してください。Firefox でテストを行うのであれば、HTTPS上のWebサーバーで公開されなければいけません。これはNgrok を使い、HTTPS のURLをローカルホストへトンネルすれば簡単です。Chrome でテストする場合は http://locahost:3000 にアクセスします。

他の人に ngrok のリンクを送るか、ご自身で2つのブラウザを使ってビデオチャットをセットアップします。チャットに入ったら、画面共有ボタンを押し、どの画面を共有するか選択すると、相手側のビデオチャットに表示されます。

screen-share2.gif

対応したブラウザでスクリーン共有が自由自在

さて、これら3つのブログ記事を通して習得した技術を用いて、ChromeとFirefoxを使ったビデオチャット内画面共有ができるようになりました。(同じChromeエクステンションを使えばOperaでも)

これを活用すれば、チームコラボレーションが可能なプレゼンアプリのような物から、ブラウザベースで画面共有ができる拡張顧客サポートツールのような物に至るまで、様々なことが実現します。 簡単なものだと、ユーザー画面をプレビューする機能を追加すれば、ユーザー自身が何を今共有しているのか分かるようになります。

GitHub のレポジトリにこれら全てのコードと、過去のブログ記事で紹介したコードを載せています。是非チェックして、みなさんのビデオチャットアプリへの機能追加に活用してみてください。

みなさんがどのようなアプリを作るか楽しみです!

Facebook コメント