Native WebRTCでもWebsocket(wss)がしたい!

#これはなに?
前回は手動シグナリングを使って、Webブラウザの映像をLinuxサーバーにループバックするところを作りました。
Native WebrtcのサンプルはベタなHTTPクライアントでHTTPSにすら対応していません。なんということでしょう!
というわけで、secureなwebsocketを使ってシグナリングをします。

今回も用ん手に絞って解説します。

#client側
何も考えることはありません。HTML5なのでチュートリアルから引っ張って来ましょう。
注意するとしたらsocket.ioはWebsocketと少し違うため純粋なwebsocketを使いましょう。
wssで接続することにだけ注意してサクッと作りましょう!
前回はice candidateが全て揃ってからSDPを交換するvanilla ICEでしたが、今回はice candidateができるたびに通信する trickle iceにしましょう。

データ構造のJSONがどうなるかだけ注意してお手持ちのプログラムの要件に合わせて開発しましょう!
event.dataにデータが入ります。

```js

socket.onopen =  () => {
  // ここでイベントハンドラを登録しましょう
  // pc.ontrack() or pc.onaddstream() pc.onicecandidate()

  // create offer はこっちでやったほうが良いです。
  pc.onnegotiationneeded = () => { /* create offer */ }

  // getUserMediaをお好みで
  navigator.mediaDevices.getUserMedia({ "audio": false, "video": { width: 1920, height: 1080 } }, /* on success */, /* on failure */ );
};
socket.onmessage = (event) => {
  // ここでoffer, answer, ice candidateを受け取りましょう。
  let message = JSON.parse(event.data);
  if (message.type === "answer") {
    // サーバーからのanswerをsetする
  } else if (message.type === "offer") {
    // サーハーからのofferを受け取る
  } else {
    // ice candidate をセットする
    // メッセージを拡張するなら else if (message.candidate) みたいな感じでチェックしましょう!
  }
}
```

#server側
###ライブラリ
さて、問題のサーバーサイドです。
まんず、ライブラリですが、peer_connection_clientをいじった皆さんは socketベースのやり方にうんざりしているはずです。
そこで今回はwebsocket++ 0.7.0を使いましょう!
boostとかよぐわがんねぇですが、なんとかなるべ、と適当に選んで見ました。
幸い [wssのサンプル](https://github.com/zaphoyd/websocketpp/blob/master/examples/echo_server_tls/echo_server_tls.cpp)はあります。 ssl証明書の指定の仕方も乗ってますし、なんとかなりそうですね。

###boostのインストール
boost 1.54.0で良いみたいなのでサクッと入れてしまいましょう。

```bash
sudo apt-get install libboost-dev
```

サンプルをコンパイルして動かしてみると、問題なく動くはずです。

```bash
g++ sample.cpp -lssl -lcrypto -lboost_system --std=c++11
```

## webrtc + boostの組み合わせ
さて、問題はここからです。
組み合わせてみるとコンパイルエラーやらビルドエラーやらでさあ大変です。
理由はwebrtc側がコンパイルセット一式を内部で持っているためです。
また、パフォーマンスを上げるために例外　-fno-exceptions や -fno-rtti などが設定しているためです。

まずはboostをヘッダーオンリーにするためのフラグを設定しましょう。
そうしないと自分の環境では glibcのバージョン違いによるリンクエラーでビルドできませんでした。

```bash
-DBOOST_EXCEPTION_DISABLE -DBOOST_DATE_TIME_NO_LIB -DBOOST_REGEX_NO_LIB -DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_SYSTEM_NO_LIB     -DBOOST_SYSTEM_NO_DEPRECATED
```

次にビルドを通すため、コンパイルフラグを変えましょう。
次のフラグをbuild.ninjaのCFLAGSから削除します。

```bash
  cflags = ...

# 以下を削除、もしくはwebsocket++以外のwebrtcのビルドとフラグを分ける。
-fno-exceptions -fno-rtti  -Wstring-conversion
```

他にもwarning系があったと思いますがその辺はお好みでどうぞ。

webspcket++でサーバーを実装する際のこの行はエラーになるのでコメントアウトしましょう。
何やらGoogleさんはssl周りのライブラリも独自実装されているようで。。

```c++
//         ctx->use_tmp_dh_file("dh.pem");
```

boost関連のインクルードファイルを分けてからインクルードしましょう。

```bash
# シンボリックリンクを貼って、システムの標準ライブラリから隔離する
ln -s /opt/include/boost /usr/local/include/boost

# build.ninjaのインクルードパスを設定する。
  ldflags = ... -L/opt/include/
```

## イベントループの処理をどっちがやるか
さて、上記の設定でなんとか頑張ればビルドは通ってだいたい動きます。
ただし、大きな問題がありまして、それはwebrtcとwebsocket、両方でイベントループ(のようなもの)をもつ、ということです。

```c++
int main() {

  ... // イベントハンドラの登録やらwebrtc周りの実装

  websocket_server->run(); // websocket++ の while(true) {イベント処理}
  (rtc::Thread::Current())->Run(); // webrtcのwhile(true) {webrtcのコールバック}

  // あれ? お互いブロックするんですけど。。
}

```

ものすごくざっくりいうと (あくまでイメージです)

```c++
// webrtc
int maint () {
  void onconnection(socket, hdl_id) {
    pc.CreateOffer(..., callback);　// これは非同期
    socket.send(...); // これも非同期
  }

  // スレッド処理
  // 前回のこれ (rtc::Thread::Current())->Run();
  while (true) {
    // イベントを取得
    if (CreateOfferの実行が完了) {
      callback(); // コールバック呼び出し
    }
  }

  return 0;
}
```

```c++
// websocket++
void onconnection(hdl_id, socket, ...) {
  socket.send(...); // これも非同期
}

// websocket++の実装
whie (true) {
  // イベント取得
  event = get_event();

  // イベントハンドラ呼び出し
  onconnection(socket, hdl_id);

  // sendの処理
  doSend(...);
}
```

こうなっているので、

- websocket++のイベントハンドラの中で pc.CreateOffer()をするとコールバックが一切呼ばれない。しかし
- かといって websocketのハンドラの最後で websocket_server->run()すると socket.send()が処理されない

ということになります。

##websocketのループをメインに考える
websocketを中心に処理を書くわけですから、websocketの処理をブロックしないようにwebrtcのスレッド処理を行わせれば良いですね。
というわけでソースをガリガリ読みましょう。
src/webrtc/base/thread.h のrun()から潜っていくと while(true)なことがわかります。
で、どうやらgetMessage()的な処理がありそうです。
こんな感じの関数を実装すれば良さそうです。

```c++
void ProcessMessage() {
  auto thread = rtc::Thread::Current();
  auto msg_cnt = thread->size();
  LOG(INFO) << "process message. : last " << msg_cnt;

  while(msg_cnt > 0) {
    rtc::Message msg;
    if (!thread->Get(&msg, 0))
      return;
    thread->Dispatch(&msg);
  }
}

int main()
{
  rtc::InitializeSSL();
  
   // websocket_serverの初期化
   ...

   // イベントハンドラの登録
   onconnection = [](){
     // peer_connection 作成など

     ProcessMessage();
   }
   onmessage = [](){
     // offerの受け取りなど

     // create answer とか

     // ice candidateのセットとか
     
     ProcessMessage();
   }

    // イベントハンドラをセットしたり

   // websocketの実行
   websocket_server->run();
   rtc::CleanupSSL();
  
   return 0;
}
```

node.jsとかブラウザの実装って偉大ですね。
あと、ラムダも偉大です。

ああ、家の外で蝉が鳴いている。すっかり夏ですね。