skywayによるグループ通話でハマってた箇所がアホすぎた話
2020/07/04の日報
Hanasot開発の日報、第8日目です。
今日はお葬式と飲み会だったので遅くなりました。
やったことは昨日に引き続き
- vueプロジェクト内でskywayによるグループ通話を実装する
です。
時間としては、
- Hanasot開発 24min
- 日報ブログ 1h1min
です。
運動は
- ウォール・ウォーキング・ブリッジ 8reps * 2
です。
vueプロジェクト内でskywayによるグループ通話を実装する
ついにskywayによるグループ通話が成功しました。ソースコードはこちらです。
<template> <div> <video id="my-video" width="400px" autoplay muted playsinline></video> <p id="my-id"></p> <textarea id="their-id" v-model="roomID"></textarea> <button id="make-call" @click="makeCall">発信</button> <video v-for="participant in participants" :key="participant" :id="participant" width="400px" autoplay muted playsinline></video> </div> </template> <script> import Peer from 'skyway-js'; export default { name: 'Dialogue', data() { return { localStream: null, roomID: '', peer: {}, participants: [], } }, mounted() { // カメラ映像取得 navigator.mediaDevices.getUserMedia({video: true, audio: true}) .then( stream => { // 成功時にvideo要素にカメラ映像をセットし、再生 const videoElm = document.getElementById('my-video') videoElm.srcObject = stream; videoElm.play(); // 着信時に相手にカメラ映像を返せるように、グローバル変数に保存しておく this.localStream = stream; }).catch( error => { // 失敗時にはエラーログを出力 console.error('mediaDevice.getUserMedia() error:', error); return; }); // skyway接続 this.peer = new Peer({ key: 'skywayコンソールから発行したid', debug: 2, }); this.peer.on('open', () => { document.getElementById('my-id').textContent = this.peer.id; }); // エラー時処理 this.peer.on('error', err => { alert(err.message); }); }, methods: { makeCall() { const mediaConnection = this.peer.joinRoom(this.roomID, { mode: 'sfu', stream: this.localStream, }); this.setEventListener(mediaConnection); }, 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(); }); }, setEventListener(mediaConnection) { this.addVideo(mediaConnection); const self = this; mediaConnection.on('peerJoin', function () { self.addVideo(mediaConnection); }); } }, } </script> <style scoped> </style>
で、ハマってたのはここです。
setEventListener(mediaConnection) { this.addVideo(mediaConnection); const self = this; mediaConnection.on('peerJoin', function () { self.addVideo(mediaConnection); }); }
このsetEventListenerは「ルームに新しく参加者が入ってきた時」に発火します。mediaConnection.on('peerJoin',
の部分ですね。入室を検知したら映像を追加する処理を走らせるわけです。ここで何にハマってたかというと this の扱いでした。
peerJoinイベントが発火したときに呼び出したいのはmethodsに定義されているaddVideoメソッドです。こいつを呼ぶためには普通はthisを使います。詳しく理解していませんがthisがvueComponentオブジェクトを表すので、thisを使います。今度ちゃんと調べるので今は下記画像で許してください。
はい。ということでvueで定義したメソッドやプロパティを使うにはthis.hogehoge
を使うということですね。で、今回は何が問題だったかというと
mediaConnection.on('peerJoin', function () { // this.addVideo(mediaConnection); と書いていたところを、下記のように変更 self.addVideo(mediaConnection); });
このように.onでイベントに絡めてメソッドを呼び出した場合、thisの表す対象が変わってしまうところですね。this.では関数内しか参照できなくなってしまうのです。これをこうやって解決しました。
setEventListener(mediaConnection) { // thisをselfに入れておく const self = this; mediaConnection.on('peerJoin', function () { // setEventListener内なら参照できるのでselfは呼べる self.addVideo(mediaConnection); }); }
超単純です。あの苦労なに?
所感
煮詰まったら一旦リセットしよう
今回学んだことはこれに尽きます。煮詰まってしまったらこんな単純なことすら思いつけません。そしてお客さんの大事なお金を使ってクソコードを書き、この単純な問題を冗長な処理で回避することになります。延々と悩むくらいなら暇を見つけて仲間に聞いたりしたほうがずっといいです。
すぐ人に聞くやつは成長しないけど、ひとりでひたすら煮詰まってるやつ(僕)もだめですね。お互いプラスになるような良い頼り方を覚えていきたいです。
これでグループ通話自体はできたので明日からは
- ミュート・カメラオフ機能
- カメラ・マイク選択機能
- 退出時にvideoタグを削除する機能
あたりをやっていきます。それでは。