ある中卒がWeb系エンジニアになるまでと、それからのこと

うつ病で高校を中退したり、たこ焼き屋のオヤジにホームページとたこ焼きを作らされたり、そのホームページが訴えられそうになったり、弁護士を目指したりした後にエンジニアになった人が書くブログ

vueとskywayによるビデオ通話で参加者離脱の処理をする

2020/07/05の日報

Hanasot開発の日報、第9日目です。
やったことは

  • vueプロジェクトのskywayによるグループ通話で参加者が離脱した際の処理を追加する

です。
時間は、

  • Hanasot開発 31min
  • 日報ブログ 37min
  • 日報ブログ引っ越し準備 59min

です。
運動は

  • アンイーブン・プルアップ 左右7repsずつ * 2(右手の1セット目は初めてスタローン流でできました!)
  • フル・プルアップ 順手 8reps
  • フル・プルアップ 逆手 8reps

です。

skywayによるグループ通話で参加者が離脱した際の処理を追加する

前回の記事でskywayによるグループ通話は開通しました。これで任意のルームを作成して、複数人によるビデオ通話自体はできることになります。加えてHanasot(哲学対話web)で必要な機能は

  • ミュート・カメラオフ機能
  • 発言権を1人に限定する機能(横から口を挟ませない)
  • 発言権のリクエストをする機能(挙手マークを画面上に出すなど)
  • 発言権を他の参加者に渡す機能(基本的に挙手している人に渡す)

という感じですね。で、今日実装したのは「もし参加者が離脱した場合に、離脱した参加者の映像を消す」機能です。哲学対話では対話中の離脱は基本的に想定されないのですが、通信状態の悪化やPCの電源オフ、ブラウザを閉じるなどの動作によって離脱してしまった場合に、残った参加者で対話を続けるための機能です。それではソースコードを見ていきましょう。

      async addVideo(mediaConnection) {
        const participants = [];
        let remoteStreams = [];
        mediaConnection.on('stream', () => {
          // video要素にカメラ映像をセットして再生
          Object.keys(mediaConnection.remoteStreams).forEach( key => {
            const remoteStream = mediaConnection.remoteStreams[key];
            const remoteId = remoteStream.peerId;
            participants.push(remoteId);
            remoteStreams.push(remoteStream);
          });
        });
        this.participants = participants;
        await new Promise(resolve => setTimeout(resolve, 1000));
        remoteStreams.forEach(async remoteStream => {
          const videoElm = document.getElementById(remoteStream.peerId);
          videoElm.srcObject = remoteStream;
          videoElm.play();
        });
      },
      async removeVideo(mediaConnection) {
        const participants = [];
        let remoteStreams = [];
        // video要素にカメラ映像をセットして再生
        Object.keys(mediaConnection.remoteStreams).forEach( key => {
          const remoteStream = mediaConnection.remoteStreams[key];
          const remoteId = remoteStream.peerId;
          participants.push(remoteId);
          remoteStreams.push(remoteStream);
        });
        this.participants = participants;
        await new Promise(resolve => setTimeout(resolve, 1000));
        remoteStreams.forEach(async remoteStream => {
          const videoElm = document.getElementById(remoteStream.peerId);
          videoElm.srcObject = remoteStream;
          videoElm.play();
        });
      },
      setEventListener(mediaConnection) {
        this.addVideo(mediaConnection);
        const self = this;
        mediaConnection.on('peerJoin', function () {
          self.addVideo(mediaConnection);
        });
        mediaConnection.on('peerLeave', function () {
          self.removeVideo(mediaConnection);
        });
      }
    },

今日追加したのはこの部分です。順番に解説していきます。

まず'peerLeave'イベントを捕まえるように、setEventListenerで登録しておきます。これは参加者が離脱した際に発火するイベントです。
発火したらself.removeVideo(mediaConnection);メソッドを呼びます。中身はaddVideoとほとんど同じものです。なぜかというと

    <video
      v-for="participant in participants"
      :key="participant"
      :id="participant"
      width="400px"
      autoplay muted playsinline></video>

このように参加者の映像を出力するvideoタグをv-forで生成しているためです。参加者が減ろうが増えようがdataプロパティに登録されているparticipantsの値さえ正しければ、参加者の人数分のvideoタグを生成してくれます。addVideoとちょっと違うのは下記の部分ですね。

// removeVideoにはこのイベントを設定していません
mediaConnection.on('stream', () => {

このstreamは相手方の映像が届いたときに発火するイベントです。今回は参加者が離脱する際に発火するpeerLeaveイベントから始まるため、相手方の映像は既に表示されています。したがってstreamイベントが発火しないんです。addVideoと同じ処理でいけたら記述量が少なくなったんですが、仕方ありませんね。

今回のハマりどころと解決方法

いうほどハマってないんですが、実装するまではaddVideoでremoveVideoの処理もまかなえるだろうと思っていました。理由は前述したとおりv-forを使っているのでparticipantsの中身を適切なものに変えるだけで、videoタグの増減は行われるからです。でも単純にpeerLeaveイベントでaddVideoを呼び出しても想定通りの動作はしませんでした。そこでこんなふうにして原因を調べました。

f:id:chusotsuengineer:20200705223101p:plain
setEventListener内のconsole.log
f:id:chusotsuengineer:20200705223138p:plain
addVideo内のconsole.log
f:id:chusotsuengineer:20200705223204p:plain
出力結果。streamイベントに絡めた処理は行われていないことがわかる。

実行されるであろう箇所にひたすらconsole.logを入れるんです。単純で最強のデバッグ。jsの好きなところを挙げてといわれたら「ログの出しやすさ」は間違いなく挙げると思います。めっちゃ簡単なんですもん。

ということで、こんなふうにしてstreamイベントは参加者が離脱した時には発火しないことを見つけたら後はサクサクとできました。

所感

ログ大事だなーってのは仕事でも常々思います。期待通りの動作をしなかったら面倒でもすぐにログをいっぱい仕込んで確認した方が絶対早いです。ソースコードとにらめっこする前にデータはどこでどうなっているのかをちゃんと把握していないと意味がないですからね。

明日はミュートとカメラオフ機能の実装をやっていきます。がんばります。