やってみたらなんとかなる

プログラミングをする上で調べたこととかやったこととか

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる #4 ~データ交換編~

概要

WebRTC と React を使ってビデオ電話アプリを作成する第4回です。今回は前回取得したデータを交換します。 今回は割と重かったりします。

今回のコード

今回のコードは長くなってしまったのでここに載せてあります。
変更したのはsrc/VideoConnect.jssrc/Video.cssです。src/index.jsも変更しているのでここに載せておきます。
src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import VideoConnect from './VideoConnect'; // 変更点

ReactDOM.render(
  <React.StrictMode>
    <VideoConnect />    // 変更点
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

コードの説明

大体の流れ

今回のコードは次のような流れで動いています。
[ページを開いた瞬間]

  1. localVideoRef, localVideoStreamに映像がセットする。

[CALLボタン押下後]

  1. localPeerConnectionおよびremotePeerConnectionを作成。

  2. それぞれにicecandidateiceconnectionstatechangeを追加する。(remotePeerConnectionにはaddstreamも)

  3. localPeerConnectionにlocalVideoStreamを設定。

  4. localPeerConnectionからOfferを作成する。

  5. remotePeerConnectionからAnswerを作成する。

という感じです。

RTCIceCandidate

そもそもICEとはなんですか?という話です。

ICE (Interactive Connectivity Establishment) は、ネットワークトポロジー (通常は音声および/またはビデオのチャット) に関係なく、2 つのピアを互いに接続するための WebRTC に (他の技術があまたある中で) 使用されるフレームワークです。 このプロトコルを使用すると、ネットワークアドレストランスレーター (NAT) を使用してそれぞれのローカルネットワーク上の他のデバイスとグローバル IP アドレスを共有していても、2 つのピアが相互に接続を見つけて確立することができます。

ICE - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

とのことです。NATを超えてピアを接続するためのフレームワークってことみたいです。

このICEですが、5つの段階に分かれていて

  1. 通信できそうな候補を集める

  2. 集めた候補を交換する

  3. 受け取った候補と自分の候補を合わせる

  4. ペアを用いて接続試行

  5. 接続成功した候補ペアからいいものを決定

という手順を踏みます。
ここでの候補がRTCIceCandidateです。

RTCPeerConnection

RTCPeerConnectionはローカルコンピューターとリモートピアをWebRTCで繋ぐためのインターフェースです。

RTCPeerConnection.addIceCandidate

リモートピアをリモートピアのICECandidateを追加するメソッドです。

Peer.addIceCandidate(newIceCandidate)
  .then(() => {
    console.log("connection success"); // 成功時の処理
  })
  .catch(() => {
    console.log("connection failure"); // 失敗時の処理
  });

今回のコードではこのように使っています。

RTCPeerConnection.setLocalDescription / setRemoteDescription

PeerConnectionにローカル/リモートの情報を紐づけるメソッドです。

RTCPeerConnection.createOffer / createAnswer

WebRTCでの接続をするためにSDP offer / answerするための関数です。SDP(Session Description Protocol)とは、接続に必要なパラメータを記述する形式の一つです。

    localPeerConnection.createOffer(offerOptions)
      .then(createdOffer) // 成功時の処理
      .catch((error) => {
        console.log('createOffer Error', error); // 失敗時の処理
      })

今回はこのようなコードになっています。
成功時にcreatedOfferが実行されるようになってます。createdOfferの中身は

localPeerConnection.setLocalDescription(description)
remotePeerConnection.setRemoteDescription(description)
remotePeerConnection.createAnswer()
  .then(createdAnswer)

というような感じで、ローカルピアとリモートピアそれぞれに各々のDescriptionを設定し、リモートピアがAnswerを作成します。 Answer作成時にcreatedAnswerが実行されます。createdAnswerの中身は

remotePeerConnection.setLocalDescription(description)
localPeerConnection.setRemoteDescription(description)

というような感じで、それぞれのピアにもう片方のピアのDescriptionを設定します。

まとめ

  • 同じブラウザ内でのデータ交換をした
  • ICEすごい

参考文献

developer.mozilla.org

www.slideshare.net

次のページ→ [React] WebRTC x React でP2Pビデオ電話アプリを作ってみる #5 ~データ通信・交換編~ - やってみたらなんとかなる