SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

近未来の技術トレンドを先取り! 「Tech-Sketch」出張所

WebRTC(PeerJS)で遠隔作業支援システムを作る(実装編1)

近未来の技術トレンドを先取り! 「Tech-Sketch」出張所 第16回(中編)

1-2. PeerJSの初期化

 device.coffeemonitor.coffeeの冒頭のnew ns.DeviceClass()new ns.MonitorClass()によってDeviceClsssやMonitorClassがインスタンス化され、各々のconstructorが実行されますが、実質の処理はBaseClassのconstructorに委譲されています。

remote-monitor.coffee
class BaseClass
  constructor: ->
    @peer = new Peer {host:HOST, port:PORT, path:PATH, debug:DEBUG, config:CONF}
  ...
class DeviceClass extends BaseClass
  constructor: ->
    super()
  ...
class MonitorClass extends BaseClass
  constructor: ->
    super()
  ...

 BaseClassのconstructorでPeerオブジェクトを生成しインスタンス変数(@peer)へ保持しますが、PeerJSはこのタイミングでシグナリングサーバに自身を登録し、WebSocketを開設します。

 シグナリングサーバでは接続してきたPeerごとにユニークIDを採番した後、WebSocket経由で@peer.on('open', callback)イベントが発生し、無名関数として登録されているcallback関数が呼び出されます。

remote-monitor.coffee
class BaseClass
  ...
  onOpen: (peerIDsetting = null) ->
    @peer.on 'open', =>
      console.log "peer.on 'open' peer.id=#{@peer.id}"
      peerIDsetting(@peer.id) if peerIDsetting?
  ...
PeerJSの初期化
PeerJSの初期化

 このcallback関数では、onOpen()の引数として何らかの関数が与えられていた場合、PeerのユニークID(@peer.id)を引数にその関数を呼び出します。

 device.coffeeでは、採番されたユニークIDを画面に表示する関数をDeviceClass.onOpen()の引数として渡しているため、ユニークIDが画面に表示されます。

device.coffee
$ ->
  dc = new ns.DeviceClass()
 
  peerIDsetting = (id) ->
    $('#device-id').text(id)
  ...
  dc.onOpen(peerIDsetting)

 一方、monitor.coffeeではコールバック関数を渡していないため、デバッグコンソールにログが出るだけで画面に変化は起きません。

monitor.coffee
$ ->
  dc = new ns.MonitorClass()
  ...
  dc.onOpen()

 これらのPeerJS初期化処理もスマートグラス・監視端末の実行順序に依存関係はありませんので、お互い非同期に実行して構いません。

ファットアローとアロー

 @peer.on('open', callback)のcallback関数が、=>(ファットアロー)で定義されていることに注意してください。CoffeeScriptの関数定義構文には「->(アロー)」と「=>(ファットアロー)」がありますが、=>(ファットアロー)で定義された関数では「呼び出し元のコンテキストのthis」が関数実行時のthisにアタッチされます。

 上記のonOpenの場合、@peer.on('open', callback)を実行したthisはDeviceClassやMonitorClassから生成されたインスタンスを指しますので、=>(ファットアロー)でcallbackを定義した場合は@peer(@はthisの省略表記)でインスタンス変数として保持しているpeerオブジェクトが参照できます。

 JavaScriptでは、以下のイディオムに相当します。

remote-monitor.js相当
(remote-monitor.coffeeから実際にコンパイルされたJavaScriptとは異なります)
BaseClass.prototype.onOpen = function(peerIDsetting) {
  var self = this;
  this.peer.on('open', function() {
    if (peerIDsetting != null) {
      peerIDsetting(self.peer.id);
    }
  });
};

 一方、->(アロー)でcallbackを定義した場合、呼び出し元のthisはアタッチされないため、このcallbackが所属するPeerオブジェクト自身がthisです。そのため @peer はundefinedになり、期待した動作とはなりません。

1-3. MediaStreamの取得

 次にMediaStreamの取得を実装します。MediaStreamの取得はPeerの初期化とは並列して実行できるため、@peer.on('open', callback)イベントの発生を待たずに処理を開始して構いません。実際の処理はinitializeメソッドに定義されています。

remote-monitor.coffee
class BaseClass
  ...
  initialize: (video, initializing, waiting) ->
    initializing() # MediaStream初期化画面の表示(device画面①、monitor画面①)
    # getUserMediaのブラウザ間差異の吸収 ※3
    navigator.getUserMedia = navigator.getUserMedia ||
                             navigator.webkitGetUserMedia ||
                             navigator.mozGetUserMedia
    # MediaStreamの取得 ※4
    navigator.getUserMedia {audio:true, video:true}
    , (stream) =>
      # MediaStreamの取得に成功
      video.prop 'src', URL.createObjectURL(stream) # HTML5のvideoタグのsrcにMediaStreamを接続
      @ls = stream # 取得したMediaStreamをインスタンス変数として保持
      @ls.getAudioTracks()[0].enabled = false # 自身のマイクをOFF
      waiting() # 接続待ち画面の表示(device画面②、monitor画面②)
    , =>
      # MediaStreamの取得に失敗
      @eh "getUserMedia fail" if @eh? #エラーハンドラにメッセージを通知

※3 getUserMediaのブラウザ間差異の吸収

 MediaStreamを取得するAPIはブラウザごとに異なり、chromeやOpera for Androidはnavigator.webkitGetUserMedia、firefox for Androidではnavigator.mozGetUserMediaになります。その実装差異を吸収しなければならないことに注意してください。

※4 MediaStreamの取得

 ブラウザより映像と音声のMeidaStreamを取得します。複数のカメラを持つデバイスの場合、映像を取得するカメラを選択できる場合もあれば、どれか1つのカメラに決め打ちされてしまう場合もあります。これはブラウザの実装依存です。

メディアストリームの取得
メディアストリームの取得

 またgetUserMediaで映像や音声を取得する際は、device画面①やmonitor画面①のように、カメラやマイクへのアクセスへ明示的に許可を与えなければなりません。不正なサイトのJavaScriptからカメラやマイクが知らない間に操作されると困りますので(許可を与える画面は、ブラウザ依存です)。

 なおHTTPでアクセスしたサイトでは毎回許可を求められますが、HTTPSでアクセスした場合はカメラやマイクへのアクセス許可/拒否をブラウザが記憶する仕様になっています(が、これもブラウザ依存です)。

1-4. 初期化完了

 PeerJSの初期化とMediaStreamの取得に成功すれば、device画面②やmonitor画面②のような接続待ち画面が表示されます。PeerJSの初期化やMediaStreamの取得に失敗した場合、エラーメッセージが表示されます。

実装編2では

 今回はPeerJSを用いた「ブラウザ上で動作する遠隔作業支援システム」の前半部分(モジュールの全体構成と初期化処理)を、実装編1として取り上げました。次回の実装編2は、スマートグラスと監視端末のブラウザ間をP2P接続し、映像や音声、データを双方向に交換する部分を取り上げます。次回もお楽しみに!

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
近未来の技術トレンドを先取り! 「Tech-Sketch」出張所連載記事一覧

もっと読む

この記事の著者

松井 暢之(TIS株式会社)(マツイ ノブユキ)

TIS株式会社 コーポレート本部 戦略技術センターに所属。アーキテクチャ設計やデータモデル策定、フレームワーク構築などバックエンド側のアーキテクトとしてプロジェクトに従事していたが、現部門への異動を契機に戦略技術の検証や新規サービスの事業企画に軸足を移す。近頃はなぜか、インフラ・運用のパターン化と自動化を目...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8017 2014/09/26 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー