SkyWayのサンプルをVue.jsで書いていくチュートリアル vol2


[vol1](https://qiita.com/n0bisuke/items/6e1f56678b2eb6318594)の続きです。

## 6. API KEYの取得

https://console-webrtc-free.ecl.ntt.com/ でSkyWayのAPI KEYを取得します。

## 7. SkyWayのSDK読み込み

index.htmlの下部にskywayのSDK(`https://cdn.webrtc.ecl.ntt.com/skyway-latest.js`)の読み込みを追加します。app.jsの読み込みよりも前で読み込みます。

```index.html
省略

    <script src="https://cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script> <!--  ←追記 -->
    <script src="https://jp.vuejs.org/js/vue.min.js"></script>
    <script src="app.js"></script>
</body>
</html>
```

app.jsの先頭にAPIキーを定数として定義します。data内で定義しても良かったのですが、変更可能にしたかったのでconst定義しています。

```
const APIKEY = '取得したAPIキーをコピペ'; //追記

const app = new Vue({
    el: '#app',
    data: {
省略
```

## 8. Peerオブジェクトの生成とIDの表示

Peerオブジェクトは僕の理解だと接続するためのクライアント情報でPeerIdはクライアント固有のID（電話番号のような雰囲気）という認識です。

index.htmlにPeerIdを表示させる領域を追加します。video要素の下にしました。

```index.html
省略
        <video id="my-video" muted="true" width="500" autoplay playsinline></video>
        <p>Your Peer ID: <span id="my-id">{{peerId}}</span></p> <!--  ←追記 -->
省略
```

app.jsのdataの箇所にpeerIdを宣言

```app.js
    data: {
        audios: [],
        videos: [],
        selectedAudio: '',
        selectedVideo: '',
        peerId: '', //追記
    },
```

mountend内に以下を追記します。

`new Peer()`で接続するためのクライアント情報となるpeerオブジェクトを作成し、openの箇所でthis.peerIdにPeerオブジェクトのIDが反映されて、Vue.jsのバインディングでビュー側に反映されます。

```app.js
    mounted: async function(){
        this.peer = new Peer({key: APIKEY, debug: 3}); //新規にPeerオブジェクトの作成
        this.peer.on('open', () => this.peerId = this.peer.id); //PeerIDを反映
        
        省略
    }
```

pythonなどでローカルサーバーを起動してlocalhost:3000などでアクセスして試します。

<img src="https://i.gyazo.com/c971f6aa790f6a88277335c87c489360.gif" width="400">

リロードするたびにIDが生成されるのが分かると思います。

ここまでのapp.jsとindex.htmlを[こちら](https://gist.github.com/n0bisuke/9f818509a1a44b45e8a0b4800a23848d)にまとめておきます。

## 9. 相手を呼び出す

呼び出し相手のIDを追加できるようにするためにcalltoidを定義します。
app.jsのdataに追記します。

```app.js
    data: {
        audios: [],
        videos: [],
        selectedAudio: '',
        selectedVideo: '',
        peerId: '',
        calltoid: '', //追記
   },
```

index.htmlに相手のIDを入力するフォームと`Callボタン`を追加します。先ほどの自分のPeerIdが表示される領域のすぐ下にしました。

```index.html
省略
        <video id="my-video" muted="true" width="500" autoplay playsinline></video>
        <p>Your Peer ID: <span id="my-id">{{peerId}}</span></p>
        <input v-model="calltoid" placeholder="call id"> <!-- 追記 -->
        <button @click="makeCall" class="button--green">Call</button> <!-- 追記 -->
省略        
```

app.jsのmethods内に`Callボタン`が押された時の処理としてmakeCallとconnectの関数を定義します。

makeCallでcalltoidと手元の映像(localStrem)を指定して、相手を呼び出します。

**相手(calltoid)に自分の映像や音声(localStrem)を送る**イメージです。

makeCallの後にconnectが呼び出されます。

connectの中身はまだ書きません。

```app.js
 methods: {
    onChange: function(){
    //省略
    },
    
    connectLocalCamera: async function(){
    //省略
    },
    
    //追記
    makeCall: function(){
      const call = this.peer.call(this.calltoid, this.localStream);
      this.connect(call);
    },
    //追記
    connect: function(call){
        
    }
 }
```

<img src="https://i.gyazo.com/a8f4efb0b50271fde1e2613ab054c693.png" width="400">

ここまでのまとめが[こちら](https://gist.github.com/n0bisuke/20f676e940289aa20bf959ac27582846)です。

## 10. 相手から呼び出された時の処理

呼び出された側の処理を記載します。

mounted内にcall（相手からの呼び出し）を待ち受ける`this.peer.on('call')`を追記します。
相手からの呼び出しがあったら反応し、call.answer()でlocalStreamを返します。

```app.js
    mounted: async function(){
        this.peer = new Peer({key: APIKEY, debug: 3}); //新規にPeerオブジェクトの作成
        this.peer.on('open', () => this.peerId = this.peer.id); //PeerIDを反映
        
        //追記
        this.peer.on('call', call => {
            call.answer(this.localStream);
        });
```

ここで、呼び出された際のlocalStreamがここまで定義されてないので追記していきます。

app.jsのdataにlocalStreamを追記します。

```app.js
    data: {
        audios: [],
        videos: [],
        selectedAudio: '',
        selectedVideo: '',
        peerId: '',
        calltoid: '',
        localStream: {} //追記
   },
```

app.jsのconnectLocalCamera内で取得したstreamを使い回せるようにlocalStreamに格納します。

```app.js
connectLocalCamera: async function(){
      const constraints = {
        audio: this.selectedAudio ? { deviceId: { exact: this.selectedAudio } } : false,
        video: this.selectedVideo ? { deviceId: { exact: this.selectedVideo } } : false
      }

      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      document.getElementById('my-video').srcObject = stream;
      this.localStream = stream; //追記
    },
```


ここまでの処理で、呼び出し -> 映像/音声をレスポンスとして返すといったところが出来ました。

## 11. 相手からの映像を描画する

10で相手から映像や音声が送られて来ているのですが、表示領域が無いのでこのままだとどこにも表示されません。

まずは相手側のビデオストリームが表示される領域をhtmlに定義します。id="their-video"のvideo要素です。

```index.html
省略
        <video id="their-video" width="200" autoplay playsinline></video> <!-- 追記 -->
        
        <video id="my-video" muted="true" width="500" autoplay playsinline></video>
        <p>Your Peer ID: <span id="my-id">{{peerId}}</span></p>

省略
```

あとはここに映像を流し込みます。

先ほど定義だけしたconnectの中身を記述します。

```app.js
省略
        connect: function(call){
            call.on('stream', stream => {
                const el = document.getElementById('their-video');
                el.srcObject = stream;
                el.play();
            });
        }

省略
```

二つのブラウザを起動して試してみましょう。

* 両方のブラウザでカメラとマイクを設定して映像を表示
* 片方のPeerIDをコピー
* もう片方のCall IDのフォームにコピーしたIDを貼り付け
* Callボタンを押す

そうするとCallボタンを押した側の画面に相手側の映像ストリームが流れ込んできます。

<img src="https://i.gyazo.com/9272587f7807f9f081a3203393107835.gif" width="400">

## 12. 呼び出された側の画面にも反映させる

現状だと、片方の画面にだけ相手の画面が反映されるのでこれを呼び出された側にも反映させましょう。

呼び出された側の処理は`this.peer.on('call')`の箇所にあたります、また、connect関数内が相手側の映像の反映する箇所になります。

`this.peer.on('call')`の内部で`this.connect(call);`を呼び出すと想定した動きになります。

```app.js
    mounted: async function(){
        this.peer = new Peer({key: APIKEY, debug: 3}); //新規にPeerオブジェクトの作成
        this.peer.on('open', () => this.peerId = this.peer.id); //PeerIDを反映
        
        this.peer.on('call', call => {
            call.answer(this.localStream);
            this.connect(call); //追記
        });
```

<img src="https://i.gyazo.com/540be405c070e2ad533abd8142ba92ff.gif" width="400">

完成系コードが[こちら](https://gist.github.com/n0bisuke/88be07a6a16ee72b9bdf4fdcd12a522f)です。

## まとめ

復習も兼ねて処理を追って解説してみました。
なんとなくですが接続の流れを理解できた気がします。

認識が違うなぁみたいな箇所があればコメントもらえると幸いです！

> `相手`とか書いてるところはクライアントAとかBの方が分かりやすそう