はじめに
この資料は、WebRTC入門者LT会 #3 向けの発表資料です。
こちらの記事の内容に補足したものです。
自己紹介
- がねこまさし @massie_g
- WebRTC Meetup Tokyo, Beginners Tokyo のスタッフ
- Skywayの中の人ではありません
WebRTC とマルチストリーム:API
- JavaScript 側から見た場合
- 1つの RTCPeerConnection で、複数の MediaStream が扱える
- 1つの MediaStream に、複数のMediaStreamTrack を含めることができる
新旧 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 と呼ばれる
- 参考
- メディアストリーム(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 は ... ?
// Chrome で Unified Plan を使う場合
let peer = new RTCPeerConnection({sdpSemantics : "unified-plan"});
(半)手動シグナリングで、動かしてみる
-
GitHub Pages https://mganeko.github.io/webrtcexpjp/basic2016/dc_signaling_multistream.html
-
半手動の意味
- 最初は手動シグナリングでSDPをコピー&ペースト、データチャネルを確立
- ※同じブラウザ同士の場合は、Broadcast Channel を使って自動的に交換できる
- 参考:ブラウザの BroadcastChannel を使ってシグナリング
- その後は、データチャネルを通してSDPをやり取りしてシグナリング
- 最初は手動シグナリングでSDPをコピー&ペースト、データチャネルを確立
※デモ
シグナリングの流れ:トラック追加
-
- 送信元
- peer.addTrack()でビデオ/オーディオトラックを追加、戻り値のsenderを保持
- peer.onnegotiationnneded()が発火
- DataChannel経由で、Offer/Answerが交換される
- 最初のOffer側とは無関係に、変更があった側からOfferを送れる
- 相手側のブラウザに映像または音声が伝送される
-
- 送信先(受信側)
- peer.ontarck()イベントが発火
- 2つ目以降のトラックだった場合は、mediastream.onaddtrack()も発火(何もしていない)
- (MediaTrackが所属する)MediaSteamに対応するVideo要素がなかったら、新しく生成
- video要素で、映像または音声を再生
DataChannel 経由のシグナリング
シグナリングの流れ:トラック除去
-
- 送信元
- 除去するトラックに対応するsenderを取得し、peer.removeTrack()で除去
- peer.onnegotiationnneded()が発火
- DataChannel経由で、Offer/Answerが交換される
- 最初のOffer側とは無関係に、変更があった側からOfferを送れる
- 相手側のブラウザで映像または音声が除去される
-
- 送信先(受信側)
- 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)
- 原因不明