LT資料：Unified Plan時代のWebRTC（半）手動シグナリング 

# はじめに

この資料は、[WebRTC入門者LT会 #3](https://webrtc.connpass.com/event/98122/)　向けの発表資料です。
こちらの記事の内容に補足したものです。

- [Unified Plan時代のWebRTC（半）手動シグナリング (2018.08)](https://qiita.com/massie_g/items/e1ba90637acb060772bb)

---

# 自己紹介

- がねこまさし @massie_g 
- WebRTC Meetup Tokyo, Beginners Tokyo のスタッフ
- Skywayの中の人ではありません

---

# WebRTC とマルチストリーム：API

- JavaScript 側から見た場合
    - 1つの RTCPeerConnection で、複数の MediaStream が扱える
    - 1つの MediaStream に、複数のMediaStreamTrack を含めることができる

![peer_media_track.png](https://qiita-image-store.s3.amazonaws.com/0/19403/9cfe7a8b-f1f8-4994-9fbb-82ce500f94aa.png)

---

# 新旧 APIの比較

メディアストリーム単位から、メディアトラック単位の扱いに変更

- 旧来のAPIでのマルチストリーム
    - RTCPeerConnection の addStream(), removeStream() を使用
    - イベントは、RTCPeerConnection の onaddstream(), onremovestream()

- 新しいAPIでのマルチストリーム
    - RTCPeerConnection の addTrack(), removeTrack()
    - イベントは、RTCPeerConnection の ontrack(), MediaStream の onremovetrack()
    - ※ MediaStream の onaddtrack() もあるが、使わないのが吉

---

# ブラウザの状況

全て addTrack() / removeTrack() / ontrack() 対応済

- Chrome 70　
- Firefox 63
- Safar 12
    - MediaStream の onremovetrack() は未対応
- Edge 41 (最新は未確認）

---

# WebRTC とマルチストリーム：プロトコル

通信プロトコルから見ると、マルチストリームは単一のポートで複数のメディアを通す方式

- Video, Audio, Data を単一ポートで通信する
- BUNDLE と呼ばれる

![protocol_multistream.png](https://qiita-image-store.s3.amazonaws.com/0/19403/64f925cd-4516-3d88-21cb-477016f461ba.png)


- 参考
    - メディアストリーム(RTP)と、制御情報(RTCP)を同じポートに載せる方式もある
    - MUX と呼ばれる

---

# マルチストリームとSDP

- BUNDLE を使うことを示すには、SDPでやり取りする
- そのやり方が2通り
    - Unified Plan ... WebRTC標準。Firefoxが採用
    - Plan B ... Chrome、Safariが採用
- 今後は Unified Plan に統一される見込み
    - Chrome 70では オプション指定で Unified Plan が利用可能
    - Chrome 72から Unified Plan がデフォルトになる見込
    - Safari TP 67 から実験的な実装が開始
    - Edge は ... ?

```js

// Chrome で Unified Plan を使う場合
let peer = new RTCPeerConnection({sdpSemantics : "unified-plan"});
```
---

# （半）手動シグナリングで、動かしてみる

- [Qiita記事： Unified Plan時代のWebRTC（半）手動シグナリング (2018.08)](https://qiita.com/massie_g/items/e1ba90637acb060772bb)
- GitHub Pages https://mganeko.github.io/webrtcexpjp/basic2016/dc_signaling_multistream.html

- 半手動の意味
    - 最初は手動シグナリングでSDPをコピー＆ペースト、データチャネルを確立
        - ※同じブラウザ同士の場合は、Broadcast Channel を使って自動的に交換できる
        - 参考：[ブラウザの BroadcastChannel を使ってシグナリング](https://qiita.com/massie_g/items/925c12076af669106b40)
    - その後は、データチャネルを通してSDPをやり取りしてシグナリング

※デモ

---

# シグナリングの流れ：トラック追加

- 1. 送信元
    - peer.addTrack()でビデオ/オーディオトラックを追加、戻り値のsenderを保持
    - peer.onnegotiationnneded()が発火
    - DataChannel経由で、Offer/Answerが交換される
        - 最初のOffer側とは無関係に、変更があった側からOfferを送れる
    - 相手側のブラウザに映像または音声が伝送される
- 2. 送信先（受信側）
    - peer.ontarck()イベントが発火
        - 2つ目以降のトラックだった場合は、mediastream.onaddtrack()も発火（何もしていない）
    - （MediaTrackが所属する）MediaSteamに対応するVideo要素がなかったら、新しく生成
    - video要素で、映像または音声を再生

---

# DataChannel 経由のシグナリング

<img width="961" alt="dc_signaling.png" src="https://qiita-image-store.s3.amazonaws.com/0/19403/d6e59253-a8b9-79da-3dd6-ccb19013a32f.png">

---

# シグナリングの流れ：トラック除去

- 1. 送信元
    - 除去するトラックに対応するsenderを取得し、peer.removeTrack()で除去
    - peer.onnegotiationnneded()が発火
    - DataChannel経由で、Offer/Answerが交換される
        - 最初のOffer側とは無関係に、変更があった側からOfferを送れる
    - 相手側のブラウザで映像または音声が除去される
- 2. 送信先（受信側）
    - mediastream.onremovetrack()が発火
    - 該当するmediastreamのトラック数がゼロになったら、対応するVideoElementの再生を停止、削除

---

# Plan B のSDP (Chrome)

Video + Audio, Video, Audio の3ストリームを送信

```
a=group:BUNDLE data video audio

m=application 59191 DTLS/SCTP 5000
a=mid:data

m=video 59203 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 … 118 114
a=mid:video
a=ssrc:1934825139 msid:4QD5cPipYYHfxxxx 1bc7a4bb-545c-yyy
a=ssrc:1928081165 msid:5b439d1a-4c68-zzz 190251fa-7934-434c-aaa

m=audio 59207 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=mid:audio
a=sendrecv
a=ssrc:3639648018 msid:4QD5cPipYYHfxxx a69cca57-009f-442e-bbb
a=ssrc:3896373756 msid:037f3b64-feee-ccc 7c449c50-8607-ddd
```

---

# Unified Plan のSDP (Firefox)

Video + Audio, Video, Audio の3ストリームを送信

```
a=group:BUNDLE sdparta_0 sdparta_1 sdparta_2 sdparta_3 sdparta_4
a=msid-semantic:WMS *

m=application 64895 DTLS/SCTP 5000
a=mid:sdparta_0

m=video 64922 UDP/TLS/RTP/SAVPF 120 121 126 97
a=mid:sdparta_1
a=msid:{968e2268-4336-4d48-xxx} {c40feace-2302-4ca6-yyy}

m=audio 64924 UDP/TLS/RTP/SAVPF 109 101 0 8 9
a=mid:sdparta_2
a=msid:{968e2268-4336-4d48-xxx} {5550b468-49a8-zzz}

m=video 0 UDP/TLS/RTP/SAVPF 120 121 126 97
a=mid:sdparta_3
a=msid:{5f905219-2416-4d96-bbb} {8798f142-402b-419d-ccc}

m=audio 0 UDP/TLS/RTP/SAVPF 109 9 0 8 101
a=mid:sdparta_4
a=msid:{0721cc21-5cb1-473d-cccc} {423518ba-9ca7-41e6-cccc}
```

- 各 video, audio 毎に m= の行がある。利用するコーデックなどはこの単位で選択できる
    - → 1つのPeerConnectionで、異なるコーデックのビデオを一緒に送ることができる（仕様上）


---

# 終わりに

- Unfied Plan で Chrome - FireFox 間でも通信できるはず
- Chome 70 - Firefox 63 間で動いた
- Safari ではこのデモは動いていない
    - データチャネルの確立に失敗したり
    - addTrack()すると接続が切れてしまったり (disconnected)
    - 原因不明