JavaScript
WebRTC
Swift
Vapor
VaporDay 17

Vapor でWebRTCを使ったビデオチャットを作成したまとめ


はじめに

ビデオチャット機能を実装したいとの要望があり、 WebRTC について調べました。

WebRTC はとても面白く、魅力的な技術でしたが、同時にとても手強かったため、忘備録&記憶の整理用この記事を書きました。

同じ頃に Swift製 WebFrameWork である Vapor に興味が沸き、「WebRTC のサーバーサイドを Vapor で書けば一度に両方の技術を取得できるのでは?」と思い、WebRTC 用のシグナリングサーバーを Vapor で作成しました。

作成した Swift 製のシグナリングサーバーはこちらです。

https://simple-video-chat.work/

4文字以上の任意のルーム名を URL の後ろに入力し iOS 以外の異なるブラウザ同士でアクセスするとビデオチャットが始まります。

Ex: https://simple-video-chat.work/xxxx

勉強用に作成したため、いつまで残すかわかりません。ソースコードはこちらです。

O-Junpei/simple-video-chat-server


ハンズオンで WebRTC の概要を理解する

@massie_g さんと @yusuke84 さんのハンズオンが素晴らしいのため、まず最初に取り掛かる事をお勧めします。

ブラウザ間でビデオチャットが表示されると感動します。

WebRTCハンズオン 概要編

https://qiita.com/massie_g/items/916694413353a3293f73

WebRTCハンズオン 準備編

https://qiita.com/massie_g/items/6141a1020dd8bdf6574e

WebRTCハンズオン 本編

https://qiita.com/yusuke84/items/43a20e3b6c78ae9a8f6c


シグナリングサーバーを作成する

(上記ハンズオンに目を通した前提で進めます。)

WebRTCハンズオン本編 STEP3 で作られているようなシグナリングサーバーを Vapor で実装します。

シグナリングサーバーは通信相手の情報を交換する役割、ちょうどブラウザ同士の電話番号(SDPと呼ばれます)を交換するイメージです。

今回は同じルーム(URL)にアクセスしている相手にSDPを転送するシグナリングサーバーを作成しました。


configure.swift

var websocketClients: [String: [WebSocket]] = [:]

// WebSockets
let wss = NIOWebSocketServer.default()
wss.get("socket", String.parameter) { ws, req in
let room = try req.parameters.next(String.self)
// roomがなければ初期化、既にある場合はcloseを削除
if websocketClients[room] == nil {
websocketClients[room] = []
} else {
websocketClients[room] = websocketClients[room]!.filter({
!$0.isClosed
})
}
websocketClients[room]!.append(ws)
ws.onText { ws, text in
for client in websocketClients[room]! {
if client.isClosed {
return
}
if ws === client {
print("slip sender")
} else {
// roomが一緒
client.send(text)
}
}
}
}
services.register(wss, as: WebSocketServer.self)


参考: Vapod Docs Using WebSockets

参考: VaporでWebSocketを使ったチャットアプリを作る


HTML ページの生成

シグナリングサーバーができたので、次はフロント(HTML)をのレンダリング機能を作成します。

routes.swiftは Rails における config/routes.rb で、ルーティングの設定を行うことができます。

4文字以上のルーム名が入力された場合はビデオチャットページ index.html へ、それ以外は Vapor のデフォルトページ welcome.heml をレンダリングしています。

index.htmlの中身(JS)はハンズオンからお借りしました。


routes.swift

import Vapor

/// Register your application's routes here.
public func routes(_ router: Router) throws {
// Top
router.get { req in
return try req.view().render("welcome")
}

// room
router.get("/", String.parameter) { req -> Future<View> in
let room = try req.parameters.next(String.self)
if room.count < 4 {
return try req.view().render("welcome")
}
return try req.view().render("index")
}
}


参考: Getting Started | Leaf


デプロイ

作成した Vapor のアプリケーションを Docker化し、GCPのCompute Engin にデプロイしました。

以下の構成になっています。

Untitled Diagram.png

以下のコマンドでローカルでも実行できます。

$ docker run -d -p 80:80 kabigon/simple-video-chat:latest


完成

Android のChorme と PC の Chorme で繋げています。

スクリーンショット 2019-03-12 9.49.17.png


早く知りたかったこと、詰まったこと


シグナリングサーバー(WebSocket)のデバックがしたい

wsta は シンプルな WebSocket クライアントです。

HomeBrew で簡単にインストールすることができ、シグナリングサーバーのデバッグに役立ちます。

$ brew tap esphen/wsta https://github.com/esphen/wsta.git

$ brew install wsta

ターミナルを2つ開き、WebSocket で接続する。

片方に入力した文字が転送される。

$ wsta wss://simple-video-chat.work/socket/xxxx // or

$ wsta ws://localhost:8080/socket/xxxx


WebRTC は localhost 以外はSSL 必須

WebRTC を使うには SSLが必要です。

オンラインで使う場合はlet's encrypt などでSSL化してあげましょう。


WebRTC のデバッグをしたい

chrome://webrtc-internals/ と打ち込むとWebTRTC のデバッグができます。

参考: WebRTCデバッグ入門


NginxによるリバースプロキシとWebSocketを同時に使用する時は設定が必要


WebSocketのハンドシェイク時に使われる Upgrade ヘッダと Connection ヘッダ は Hop-by-Hop ヘッダ である。 通常のEnd-to-End headerと異なり基本的に一度の転送に対して有効なヘッダなため、ReverseProxyを間に挟んでいる場合これらのheaderはバックエンドサーバまで届かない。


Nginxによるリバースプロキシ + WebSocket を使う時は注意が必要です。

nginx.conf ファイルに以下を追記します。

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

参考: Nginxを用いたWebSocketサーバのReverseProxy構成及びSSL/TLS接続


iOS のブラウザ(Safari & Chorme)では WebRTCは使えない場合がある

制限が多く、思ったように動かない場合が多いため注意が必要です。

(追記: Safari 12.1 で映像コーデック VP8 に対応することで、 WebRTC は主要ブラウザで問題なく利用することができるようになる。とのことです。)

参考: Safari と WebRTC について

参考: WebRTC の今


iOS アプリでWebRTC を使ったビデオチャットアプリを作りたい

iOS アプリのブラウザ(WebView)では制限が多いため、WebRTC に対応したブラウザを CocoaPods などでインストールする必要があります。

以下の記事がとても良かったのでおすすめです。

SwiftでWebRTC実装ハンズオン 事前準備編

https://qiita.com/tnoho/items/3b94371e59fe8ad6ce03

SwiftでWebRTC実装ハンズオン 本編

https://qiita.com/tnoho/items/f5afa3ba749eed9b9716

ただ、上記記事のソースコードは Swift3 で書かれており、少し古くなってしまっているので、Swift4.2 で書き直しました。

近いうちにこの記事の続きとして公開します。


やっぱり辛い逃げよう

WebRTCを自前で実装すると大変です。