WebRTCでツイッターにシェアできるインスタントボイスチャットサービスを作るぞ
構想
ツイッターに先着一人だけ通話に参加できる部屋が作れるサービスを作りたい。
先着一名だけ通話につながる
既に誰かが先に接続してたら、通話の募集が始まったら通知が来るのでアプリをインストール(PWA)しようと促される
通話を終えたら、部屋ごと削除する。
新しい部屋を作るともう一度シェアさせる導線
https://gyazo.com/27fe5e8b7d12d9d72fcb2fbb15272531
名前案
シャベリー
ちょいトーク
実装の構想
WebRTCの実装はとりあえずSkywayでやってみる
→有料枠に達したら、ConohaでSTUNサーバーとかをホスティングしようかな
firebase
nuxt
あたりを使ってすませたい
以下調べていったことを順次まとめていく
WebRTCしくみ
Media Capture and Stream
ブラウザやカメラにアクセスするためのAPI規格です。getUserMedia() で取得できる。
Session Description Protocol(SDP)
通信に必要な各ブラウザの情報を示す文字列。
(セッションの属性、メディアの形式、IPアドレス、ポート番号、通信帯域など)
Interactive Connectivity Establishment(ICE)
相手ブラウザに到達する可能性のある通信経路に関する情報を示したもの
STUN
https://gyazo.com/795ea0767a74644315c4000576430a2f
TURN
https://gyazo.com/b2fb9fda7e43a7747c7dedc4649d0ad9
流れ
Signaling Serverを用いてSDPとICE Candidateを交換する
ICE Candidate
NAT配下の場合、自身のIP/PortをSTUNサーバを用いて取得する
ポート制限等におけるNAT越え通信実現の際は、TURNサーバがデータを中継して送信する経路になる
https://gyazo.com/1297a80f95d75bff226f6a6ed25bbad0
https://gyazo.com/77e6d48e828a282650d2ba668918ef54
https://gyazo.com/02baa8dfa53119d455d42f2cdc1a5cde
上記のより実装的な話
ストリームの再生
code:js
const $video = document.createElement('video');
$video.autoPlay = true;
// 自身のストリーム or
// なんらかの手段で取得したP2P相手のストリーム
$video.srcObject = stream;
// or
$video.src = URL.createObjectURL(stream);
audioタグで音声を再生できる
audio`要素にもそのままストリームをつっこんで良くて、AudioTrackがあれば音が流れる。
もちろん、WebAudioAPIのMediaStreamAudioSourceNodeも使える。
こうすると音声レベルとかも取れるのでデバッグにも便利。
code:js
const ctx = new AudioContext();
const source = ctx.createMediaStreamSource(stream);
source.connect(ctx.destination);
参考:
Nodeを接続し、AudioDestinatinationに最終的に出力する
途中にエフェクトをかけたり, ビジュアライゼーションするノードを挟める
https://gyazo.com/30eba392c91f8d0e54efae7d907398dc
まとめ(引用)
送りたいストリームを用意
通信相手ごとにRTCPeerConnectionを用意
RTCPeerConnectionの各メソッド、イベントハンドラを駆使して準備
そんなに多くないし、覚えられる
自身のストリームをピアにつなぐ
Offer / Answerの決まった手順でSDPをやり取り
iceConnectionStateとかsignalingStateとかで進行状態も取れる
加えて、ICE Candidateも必要に応じてやり取り
ICEをどのタイミングで送るかで処理の流れが変わる
それができるとストリームが相互につながる
自身のストリームと同じく、相手のストリームも表示するなり
ボイスチャットの実装
https://gyazo.com/d31b633cb4bad14da98487000d897c76
https://gyazo.com/acd7b486c339cec15c280b19929c2532
https://gyazo.com/5b925fb1dd5f899216dba93946db3e73
https://gyazo.com/daecdad99a95fdf17c08994f9e4fd7e1
https://gyazo.com/f580f375fd686f8ba40ad189e46e1c89
https://gyazo.com/c6cdf9ace2af4cfbdc5593217146ae82
SkyWayの実装
https://gyazo.com/38fe5093db2692cb9d3a400b16d83e4b
Peer と Roomを作っていい感じにしてる。
roomのイベント監視して、渡ってきたobjectを使って処理している
Componentsはステートレス、PeerやRoomはステーフルなので、扱い注意
https://gyazo.com/81b4df416584520d3815331dc50c1c2f
https://gyazo.com/d00c9921c455dd05113444d1a1bcf02e
https://gyazo.com/59942cb5982740d453be0cd44f30b4bf
https://gyazo.com/a0cd8fa49580b3cdd8aec75e18edda47
https://gyazo.com/bd0846f20f3dadf7a8b3e5377ec05cd9
ベースにしたSkywayでのビデオチャット
1対1のPeerで実装
オンライン判定
通話を開始するまえに、通話を募集したユーザーがオンラインかどうか判定したい
https://gyazo.com/01b8f74af737af89dd914d9a2e805329
Firebase側で実装するのはつらそう
skywayのpeerIdが生きていれば、通話を募集しているユーザーはオンラインである事がわかる
しかし、skywayにはPeerIDが生存してるかどうかを判定するメソッドがドキュメントを読んだかぎりなさそう
思いついた実装方法
metadataで{type: 'check-alive'}とかを指定してconnectする
受け取った方は、aliveを返す
上記定期実行する
その他
命名の参考に
CallSid: 通話の識別子
AccountSid: ユーザのTwilioアカウントSID
From: 発信者
To: 相手先
CallStatus: 通話の状態
ApiVersion: Twilio APIのバージョン
Direction: 発着信の種別
ForwardedFrom: 転送された場合の転送元(日本では利用不可)
CallerName: 発信者IDの検索結果(オプション)
queued: 通話は発信待ち(statusCallbackEventにinitiatedを指定した時のみ)
ringing: 相手先を呼び出し中(statusCallbackEventにringingを指定した時のみ)
in-progress: 相手先が応答し、通話中(statusCallbackEventにansweredを指定した時のみ)
completed: 相手先が応答し、通話が正常に終了
busy: 相手先からのビジー応答(話中)
failed: 通話不成立(番号違いなど)
no-answer: 相手先不応答
canceled: キューイング中、もしくは呼び出し中に発信をキャンセルした
作業リスト
o 通話を切ったときに、10秒くらい遅れて向こうに伝わる