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

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

使っている環境について

概要

現在(2020/10/27)使っている環境です。もしこのブログを参考にしてる人がいたらここの環境を参考にしてください。たまに更新し忘れると思います。どこまで書けばいいかわかんない。

相棒

ノートPC

MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
CPU : 2.3Ghz クアッドコアIntel Corei7
メモリ : 32GB 3733MHz LPDDR4X
GPU : Intel Iris Plus Graphics 1536MB
OS : macOS Catalina ver.10.15.7

大体いつもこの子で作業をしてます。
macを使うのはこの子が初めてだったりします。
この前に使っていたノートPCがsurfaceだったので大裏切りをかましました。

開発環境

Python

python 3.7
conda 4.9.0
pip 20.0.2
...こんなところ...?

C++

gcc 9.2.1
C++においてはAtCoderについてるコードテストとかだけだったり

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる 番外編#3 ~MUIで見た目を整える~

概要

WebRTCとReactでビデオ電話アプリを作る番外編第3回です。今回はReactのUIライブラリであるMaterial-UIを用いて外見を整えていきます。

MUIのインストール

frontendディレクトリでnpm install @mui/material @emotion/react @emotion/styled でOKです。とても簡単

コードの変更

今回のコードはGitHubfrontend/src/VideoConnect5.jsに記載してます。
大きく変えたのは以下の部分です。 VideoConnect5.js

import React from 'react';
// import './Video.css';
import socketClient from 'socket.io-client';
import { Box, Button, Grid, Card, CardMedia, CardActions, Typography, CardContent } from '@mui/material'; // 追加

// 省略
// 以下大幅変更
return (
    <div className="VideoView">
      <Grid container spacing={2} alignItems="flex-start" justifyContent="center">
        <Grid item xs>
          <div>
            <Card sx={{ width: 400, height: 280 }}>
              <CardMedia sx={{ height: 225 }} component="video" playsInline autoPlay ref={localVideoRef} />
              <CardActions>
                <Button variant="contained" onClick={calling} disabled={!canCalling}>CALL</Button>
                <Button variant="contained" onClick={allowJoin} disabled={!isKnocking}>ALLOW</Button>
              </CardActions>
            </Card>
          </div>
        </Grid>
        <Grid item xs>
          <Card sx={{ width: 400, height: 225 }}>
            <CardMedia sx={{ height: 225 }} component="video" playsInline autoPlay ref={remoteVideoRef} />
          </Card>
        </Grid>
      </Grid>
    </div>
  )

まとめ

  • MUIを使うと簡単に見た目を整えられる!すごい!

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる 番外編#2 ~別ブラウザにも対応させる~

概要

WebRTCとReactを使ったビデオ電話アプリ制作の番外編第2回です。今回はブラウザの対応範囲を増やしたいと思います。
これまでのコードは基本的にChrome向けのコードだったのでMacユーザーの方はもしかしたら途中で意味不明エラー(これは関数じゃないよ的な)が出ていたと思います。(かくゆう私もMacユーザーですが)
それをバシッと今回解決します。

そもそもなんでそんなことが起こるのか

ここでWebRTCの関数などについて調べるとわかるのですが、どうやらブラウザごとに対応バージョンが違うらしく、Chromeが一番最新まで対応しているということみたいです。(WebRTCもChromeGoogle製みたいなところですからね)

解決方法

Google側もこの状況をほっといているわけではなく、adapter.jsというコードを公開しています。このコードを読み込んであげるだけでブラウザを気にせず最新バージョンで書いてOKって感じみたいです。
なので、今回はこのコードを読み込んで解決していきます。

今回のコードはGitHubVideoConnect4.jsとしてアップしています。 VideoConnect4.js

// 省略
React.useEffect(() => {
    navigator.mediaDevices.getUserMedia(constraints)
      .then((stream) => {
        localStream = stream;
        console.log(localStream);
        localVideoRef.current.srcObject = stream;
        setCanCalling(true);
      })
      .catch((error) => {
        console.log("ERROR", error);
      });
    const adapterScript = document.createElement('script'); // 追加
    adapterScript.src = "https://webrtc.github.io/adapter/adapter-latest.js"; // 追加
    adapterScript.async = true; // 追加
    document.body.appendChild(adapterScript); // 追加
    return () => { // 追加
      document.body.removeChild(adapterScript); // 追加
    }; // 追加
  }, []);
// 省略

fontend/src/index.js

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

import VideoConnect4 from './VideoConnect4'; // 変更

ReactDOM.render(
  <React.StrictMode>
    <VideoConnect4 /> // 変更
  </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();

これでOKです。どのブラウザからもうまく動くようになったと思います。

まとめ

  • adapter.jsのおかげでブラウザを気にせず実装ができる。素晴らしい。

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる 番外編#1 ~Herokuにデプロイ~

概要

WebRTCとReactを使ったビデオ通話アプリケーションの番外編第1回です。これまでとりあえずうまく通話ができた(localhost内)ので今度はインターネットに公開してみます。
無料で使用できるHerokuというサービスで公開します。

Heroku

Herokuは基本料金無料で使用できるクラウドアプリケーションサービスです。

会員登録

ここから会員登録をします。必要事項を入力してください。

デプロイ

ダッシュボード右上のNewボタンからCreate New Appを選択します。
このページでアプリの名前と使用するサーバ(アメリカorヨーロッパ)を指定します。
その後のページでは、DeployタグのDeployment method欄でデプロイの方法を選択できるのですが、おすすめはGitHub連携です。(それしか使ったことがないなんて言えない)
この方法だと連携したGitHubレポジトリが更新されるたびにHeroku側も更新してくれるため楽です。

デプロイするコード

本編で使用したコードをそのままデプロイしても全然動きません。なので少し手を加えていきます。
と言っても手を加えるのはごく一部です。
backend/index.js

// 省略
const io = require('socket.io')(server, {
  cors: {
    //origin: 'http://localhost:3000',から変更
    origin: 'http://<appの名前>.herokuapp.com',
    methods: ['GET', 'POST'],
  }
});

app.use(express.static(path.join(__dirname, '../frontend/build'))); // 追加
// 省略

VideoConnect2.js

// 省略
// "http://localhost:3001"から変更
const SERVER = "https://<appの名前>.herokuapp.com";
// 省略

/package.json

// 省略
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node backend/index-heroku.js",
    "heroku-postbuild": "npm install && cd frontend && npm install && npm run build"
  },
// 省略

fontend/index.js

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

import VideoConnect3 from './VideoConnect3'; // 変更

ReactDOM.render(
  <React.StrictMode>
    <VideoConnect3 /> // 変更
  </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();

上記の部分を変更してください。<appの名前>はHerokuでのApp作成時の自分で決めたAppの名前を入力してください。変更後のものをGithubbackend/index-heroku.jsfrontend/src/VideoConnect3.jsとして保存してます。
最後にbackend内でnpm run buildを実行してください。実行後にbuildフォルダができれば完成です。

動作確認

ダッシュボード右上のOpen appをクリックすると自分のAppが開きます。同じURLでもう一つタブを立ち上げ本編第5回と同様に使用してみましょう。うまく動くはずです。 うまく動かない時は、Herokuのダッシュボード右上MoreからView logsにエラーが出ていると思います。

まとめ

  • Herokuは偉大なサービスです。無料で利用できてこんな簡単にデプロイできるのすごい

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

概要

WebRTCとReactを使用したビデオ電話アプリ製作の第5回です。今回は、別のブラウザ間でのアクセスを可能にします。 今回もだいぶ重いです...

追加の準備

今回はサーバーにexpress、WebSocketを使用するためにsocket.ioを使用します。そのために追加で準備です。 まずは、react-webrtc(トップディレクトリ)内で

npm install express socket.io

を実行します。(express、socket.ioのインストール)
次にfrontend内で

npm install socket.io-client

を実行します。(socket.ioのクライアント側をインストール)
最後に、react-webrtc(トップディレクトリ)内のpackage.jsonの"script"に

"script": {
  "start": "node backend/index.js"
}

を追記します。
これで準備は完了です。

今回のコード

今回のコードも長くなってしまったので、ここに載せてあります。
変更(追加)したのはfrontend/src/VideoConnect2.jsbackend/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';
import VideoConnect2 from './VideoConnect2'; // 変更点

ReactDOM.render(
  <React.StrictMode>
    <VideoConnect2 />   // 変更点
  </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. 2つのターミナル①、②を準備します。
  2. ①でトップディレクトリでnpm start
  3. ②でfrontend内でnpm start
  4. ブラウザでhttp://localhost:3000にアクセス
  5. Callボタンを押す
  6. 他のタブでhttp://localhost:3000にアクセス
  7. Callボタンを押す
  8. 1つ目のタブでAllowボタンを押す
  9. つながる!

大体の流れ

  1. ページを読み込む時にカメラの映像をローカルビデオとしてセット
  2. Callボタンを押すと、サーバとの通信を始める
  3. サーバに部屋がなければ作成して自分がホストになる。
  4. サーバに部屋があれば参加する。
  5. SDPを交換してリモートピアのビデオを受けとる。
  6. リモートビデオを表示する。

socket.on() socket.emit()

socket.ioを用いてwebsocketで通信する時に使用する関数です。
socket.emitで関数を呼び、その呼び出される関数をsocket.onで記述します。

まとめ

  • socket.ioを用いてサーバと通信することで別のタグなどから同時にアクセス可能

[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 ~データ通信・交換編~ - やってみたらなんとかなる

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる #3 ~映像・音声取得編~

概要

WebRTCとReactを使った簡単なビデオ通話アプリ作成の第3回です。 今回は、映像の取得まで行います。

今回行うこと

  • getUserMedia()を使った映像と音声の取得

今回使用するコード

次の①、②のコードを書いた後にnpm startでカメラに映った自分が表示されると思います。

①src/VideoView.js (新規作成)

import React from 'react';

export default function VideoView() {
  const videoRef = React.useRef(null);
  const constraints = {
    video: true,
    audio: false,
  }

  React.useEffect(() => {
    navigator.mediaDevices.getUserMedia(constraints)
      .then((stream) => {
        videoRef.current.srcObject = stream;
      })
      .catch((error) => {
        console.log(error);
      })
  }, []);

  return (
    <div className="VideoView">
      <video playsInline autoPlay ref={videoRef} />
    </div>
  )
}

②src/index.js (編集)

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

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

ReactDOM.render(
  <React.StrictMode>
    <VideoView />  // 変更点
  </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();

コードの中身

React.useRef

useRefはReact内でタグのパラメータを操作する時に使用します。 タグの中に<video src="./hogehoge.mp4" />のように書き込めればそれでいいのですが、 今回使用するパラメータsrcObjectはそのように書き込めません。 そんな時に便利なのがuseRefです。
タグに<video ref={videoRef}>のようにrefパラメータを記述し、その変数videoRefを受け取ります。 そこからvideoRef.current.srcObject = streamのようにパラメータに値を代入するとという使い方です。

React.useEffect

useEffectは「この変数の値が変更されたらこの処理を実行したい」という時に使います。

React.useEffect(() => {
  // 実行したい処理
}, [. 変更を検知したい変数達. ]);

こんな感じで使用します。変更を検知したい変数はArrayとして複数指定することが可能です。
今回のコードのように空のArrayが指定された場合は、最初に読み込まれた時のみ処理が実行されます。

navigator.mediaDevices.getUserMedia

今回の主役です。ユーザーのビデオもしくはオーディオを取得します。

navigator.mediaDevices.getUserMedia(取得制約)
      .then((stream) => {
        // 取得成功時の処理
      })
      .catch((error) => {
        // 取得失敗時の処理
      })

ビデオ等を取得する時にはブラウザ側から「ビデオとか取得するけどOK?」という確認があります。 この確認で拒否されてしまった場合やそもそもビデオがない時などに「取得失敗時の処理」行われます。
取得制約は何を取得するのかをjson形式で指定します。

{
  video: true,
  audio: false
}

のように指定すると、「ビデオは取得するけど、オーディオは取得しません」という意味になります。
今回の場合、audio: trueにするとハウリングが起こるので注意してください

まとめ

今回はgetUserMediaを用いてビデオ・オーディオの取得を行いました。次回は取得したデータを相手に渡します。

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

[React] WebRTC x React でP2Pビデオ電話アプリを作ってみる #2 ~準備編~

# 概要 第2回は準備編です。環境構築などを進めます。

今回やること

  • node.jsのインストール
  • 作業ディレクトリの作成
  • Reactのインストール

node.jsのインストール

Reactを使用するのにはnode.jsが必要です。このインストールはOSごとにやり方が違うので公式とかここ(Windows)とかここ(Mac)とかを参考にしてみてください。参考までに僕がインストールした手順を残しておきます。(僕のPCのOSはmacOS Montereyです。)

①nodebrewのインストール

brew install nodebrew

②node.js(最新版)のインストール

nodebrew install-binary latest

これでOKのはずです。

作業ディレクトリの作成

適当な場所に作業ディレクトリを作ります。 僕は、Documentsディレクトリ内にreact-webrtcというディレクトリを作成しました。

npm init -y

でpackage.jsonを作成しておきましょう。

Reactのインストール

Reactをインストールします。(Reactをインストールって言い方あっているのかとても不安です。教えてください詳しい方...)

npx create-react-app frontend

この後、

cd frontend

でfrontendディレクトリに移動した後

npm start

その後、ブラウザでhttp://localhost:3000にアクセスして のような画面が表示されればOKです。

まとめ

ひとまずこれで準備完了です。次回から製作に入る予定です。

次のページ→ [React] WebRTC x React でP2Pビデオ電話アプリを作ってみる #3 ~映像・音声取得編~ - やってみたらなんとかなる