アスキーアートのビデオチャット
アスキーアートのビデオチャットを作った。 サイトのURLは /pages/aachat/ 。
概要
AA電話は、WebRTCを使用してリアルタイムでビデオをASCIIアートに変換し、相手と通信するWebアプリケーション。
音声は普通に通話できるけど、通常の映像を無駄にテキストに変換して表示します。
処理的には普通に映像を表示するほうが楽だが、を敢えてAAで表示することにより、低解像度であることを誤魔化しつつ帯域を節約することができる。
使ってみるとわかるが、相手の表情がわからないため、ずっと喋ってないと不安になる。
技術構成
使用技術
- WebRTC: P2P通信
- Piping Server: シグナリング用途に転用
- Canvas API: 映像処理
- MediaDevices API: カメラ・マイク制御
システム構成図
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#4a90e2', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#6bb6ff', 'lineColor': '#8cc8ff', 'background': '#2d3748', 'mainBkg': '#2d3748', 'secondBkg': '#4a5568'}}}%%
graph TB
subgraph "ブラウザA (ホスト)"
A1[カメラ/マイク] --> A2[MediaStream]
A2 --> A3[Canvas処理]
A3 --> A4[ASCII変換]
A4 --> A5[表示]
A2 --> A6[PeerConnection]
end
subgraph "Public Piping Server(※)"
S1[シグナリング]
end
subgraph "ブラウザB (ゲスト)"
B1[カメラ/マイク] --> B2[MediaStream]
B2 --> B3[Canvas処理]
B3 --> B4[ASCII変換]
B4 --> B5[表示]
B2 --> B6[PeerConnection]
end
A6 -.PUT/GET<br/>(暗号化データ).- S1
B6 -.PUT/GET<br/>(暗号化データ).- S1
A6 <==>|WebRTC P2P接続| B6
classDef hostStyle fill:#4a90e2,stroke:#6bb6ff,stroke-width:2px,color:#ffffff
classDef guestStyle fill:#9f7aea,stroke:#b794f6,stroke-width:2px,color:#ffffff
classDef serverStyle fill:#ed8936,stroke:#f6ad55,stroke-width:2px,color:#ffffff
class A1,A2,A3,A4,A5,A6 hostStyle
class B1,B2,B3,B4,B5,B6 guestStyle
class S1 serverStyle
主要機能
1. WebRTC通信
- Piping Server を使用したシグナリング
- GoogleのSTUNサーバーを使用したNAT越え
- TURNサーバーは使用せず(ファイアウォール等で接続できない場合はあきらめる)
- P2Pでの直接通信
2. ASCII アート変換
- カメラ映像を80×60文字の無駄にAAに変換
- どうせAAに変換するため、敢えての低解像度(80×60)
3. デバイス管理
- カメラ・マイクの選択機能
- 通話中のデバイス切り替え対応
通信フロー
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#4a90e2', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#6bb6ff', 'lineColor': '#8cc8ff', 'secondaryColor': '#ed8936', 'tertiaryColor': '#9f7aea', 'background': '#2d3748', 'mainBkg': '#2d3748', 'secondBkg': '#4a5568', 'actorBkg': '#4a90e2', 'actorTextColor': '#ffffff', 'actorLineColor': '#6bb6ff', 'signalColor': '#ffffff', 'signalTextColor': '#ffffff', 'c0': '#4a90e2', 'c1': '#9f7aea', 'c2': '#ed8936'}}}%%
sequenceDiagram
participant H as ホスト
participant P as Public Piping Server
participant G as ゲスト
H->>H: カメラ起動・AA変換開始
H->>P: PUT /aachat/{keyword}<br/>暗号化されたOffer(SDP)+sessionToken
G->>G: カメラ起動・AA変換開始
loop ポーリング(2秒間隔)
G->>P: GET /aachat/{keyword}
P-->>G: Offerデータ+sessionToken取得
end
G->>P: PUT /aachat/{keyword}/{sessionToken}/answer<br/>暗号化されたAnswer(SDP)
loop ポーリング(2秒間隔)
H->>P: GET /aachat/{keyword}/{sessionToken}/answer
P-->>H: Answerデータ取得
end
par ICE候補の交換
H->>P: PUT /aachat/{keyword}/{sessionToken}/ice-host<br/>ホストのICE候補
loop ポーリング
G->>P: GET /aachat/{keyword}/{sessionToken}/ice-host
P-->>G: ホストのICE候補取得
end
and
G->>P: PUT /aachat/{keyword}/{sessionToken}/ice-guest<br/>ゲストのICE候補
loop ポーリング
H->>P: GET /aachat/{keyword}/{sessionToken}/ice-guest
P-->>H: ゲストのICE候補取得
end
end
Note over H,G: P2P接続確立後は Public Piping Server 不要
H->>G: ビデオ/オーディオ直接送信
G->>H: ビデオ/オーディオ直接送信
Note over H,G: 各自でローカル変換
H->>H: 受信映像をAA変換
G->>G: 受信映像をAA変換
Piping Server の活用方法
Piping Serverは本来一時的なデータ転送を行うものですが、以下の方法でWebRTCシグナリングに転用しています:
- エンドポイント設計:
- 初回のOffer:
/aachat/{keyword} - 以降の通信:
/aachat/{keyword}/{sessionToken}/*
- 初回のOffer:
- sessionToken: 16文字のランダム文字列でセッションを分離
- HTTP PUT/GET: データの保存と取得
- ポーリング: リアルタイム通知がないため2秒間隔でポーリング
- 暗号化: キーワードをキーとしたXOR暗号化でデータを保護
- 一時的なデータ保存: Offer/Answer/ICE候補を一時的に保存
制限事項
- 同時接続は1対1のみ
- ブラウザのWebRTC対応が必要