WebRTC Meetup Tokyo #11
ブラウザでMCU 作ってみた
Build MCU on Browser
インフォコム株式会社
がねこまさし
@massie_g
1
自己紹介
• がねこまさし / @massie_g
• インフォコム株式会社 に所属
– 技術調査と社内での利用を推進するチーム
• WebRTC入門2016を HTML5Experts.jpに連載中
– https://html5experts.jp/series/webrtc2016/
• 最近Qiitaに上げた記事が、自己最高のストック数
– WebRTCを試すときにオッサンが映り続ける問題に対処する
– http://qiita.com/massie_g/items/5a6c4b69374d5997dc37
2
今日のお題
• MCU : Multipoint Control Unit
– 多地点制御装置
– ビデオ会議で良く使うやつ
• ブラウザー
– WebRTC, Canvas, Web Audio
3
P2P と MCU
4
ブラウザ
A
ブラウザ
B
ブラウザ
D
ブラウザ
C
P2Pの場合
• サーバ不要 ◎
• ブラウザ側の
• CPU負荷:高 ×
• ネットワーク負荷:高 ×
ブラウザ
A
ブラウザ
B
ブラウザ
D
ブラウザ
C
MCU
映像・音声
を合成
MCUの場合
• MCUサーバ必要 → CPU負荷:激高 ××
• ブラウザ側はCPU/ネットワーク負荷:低 ◎
モバイル
にも最適
過去の試み:WebRTC Meetup Tokyo #4
• WebRTC +αで無理やりやってみた×3
– (3) 多人数の映像を無理やり合成してみた
– http://www.slideshare.net/mganeko/webrtc-meetup4-lt (P21~)
5
いまなら、ブラウザだけでできる!
• Canvas の captureStream()
– Firefox 43~、 Chrome 51~
• リモートの音声が Web Audio API で操作可能
– Firefox 40以前からOK、 Chrome 49~
6
ブラウザMCU DEMO
7
MCUサーバー役の仕組み:Video
8
RTCPeerConnection A
MediaStream <video>タグ
RTCPeerConnection D
MediaStream
<canvas>タグ
drawImage()
requestAnimationFrame()で
継続的に描画
<video>タグ
drawImage()
MCUサーバー役の仕組み:Video
9
RTCPeerConnection A
RTCPeerConnection D
<canvas>タグ
MediaStream
captureStream(fps)で取得
MCUサーバー役の仕組み:Video
10
RTCPeerConnection A
MediaStream <video>タグ
RTCPeerConnection D
MediaStream
<canvas>タグ
MediaStream
drawImage()
requestAnimationFrame()で
継続的に描画
<video>タグ
captureStream(fps)で取得
drawImage()
MCUサーバー役の流れ:Video
• RTCPeerConnection からリモートのMediaStreamを取得
– それを <video> タグで表示
• Canvasのコンテキストを getContext('2d')で取得
• window.requestAnimationFrame()で、継続的に描画処理を実行
– drawImage()を使って、各<video>の映像を、Canvasに描画
• Canvasから captureStream(fps)を使って、MediaStreamを取得
– RTCPeerConnectionに addStream()で渡し、通信相手に送り返す
11
MCUサーバー役の注意点:Video
• window.requestAnimationFrame() 利用
– ウィンドウ/タブが隠れると呼ばれない
– 最小化したり、他のタブの後ろに回すと描画停止
• 代わりに window.setInterval() を使っても
– ウィンドウ/タブが完全に隠れると、著しく間隔が開いてしまう
– 50msに指定しても、後ろに回すと1秒以上間隔が開く
12
DEMO
MCUサーバー役の注意点:Audio
• Videoは全員に同じのものを送り返せばよい
• Audioは、全員に同じものを送り返すと … つらい
– 自分の声が「ちょっと遅れて戻ってくる」と、とてもしゃべりにくい
– WebRTC開発している皆さんは、きっと経験あると思います
– 開発していない人にも分かるように、試してみます
• https://mganeko.github.io/webrtcexpjp/basic2016/camera_mic.html
• ヘッドフォンを付けて、やってみてください
• 大変しゃべりにくい、です
13
DEMO
MCUサーバー役の仕組み:Audio
• Audioは、「マイナスワン」を作る必要あり
– 自分以外の参加者の声を合成(合算)した音
• 複数種類の音声生成が必要
– 4人いたら、4通り
– N人いたら、N通り
14
ブラウザ
A
ブラウザ
B
ブラウザ
D
ブラウザ
C
MCU
音声合成は
やっかい
映像(Video)より
音声(Audio)の方が
厄介
MCUサーバー役の仕組み:Audio
15
RTCPeerConnection A
MediaStream
AudioContext .
createMediaStreamSource()
で生成
MediaStreamAudioSourceNode
MCUサーバー役の仕組み:Audio
16
RTCPeerConnection D
MediaStream
MediaStreamAudioSourceNode MediaStreamAudioSourceNode
MediaStreamAudioSourceNode
MediaStreamAudioDestinationNode
AudioContext .
createMediaStreamDestination()
で生成
合成(合算)
MCUサーバー役の仕組み:Audio
17
RTCPeerConnection A
MediaStream
RTCPeerConnection D
MediaStream
AudioContext .
createMediaStreamSource()
で生成
MediaStreamAudioSourceNode MediaStreamAudioSourceNode
MediaStreamAudioSourceNode
MediaStreamAudioDestinationNode
AudioContext .
createMediaStreamDestination()
で生成
合成(合算)
MCUサーバー役の流れ:Audio
• RTCPeerConnection からリモートのMediaStreamを取得
• Web AudioのMediaStreamAudioSourceNodeを生成
– MediaStreamをWeb Audioのノードに変換
• MediaStreamAudioDestinationNodeを生成
– 自分以外の音のSourceNodeを接続(音を合成)
– sourceNode.connect(destinationNode);
• MediaStreamを取り出す
– RTCPeerConnectionに addStream()で渡し、送り返す
– peer.addStream(destinationNode.stream);
• ※以上を、メンバー毎に個別に行う
18
MCUサーバー役の仕組み:Video & Audio
19
RTCPeerConnection A
RTCPeerConnection D
MediaStream
(Video A+B+C+D)
MediaStream
(Audio B+C+D)
RTCPeerConnection B
RTCPeerConnection C
MediaStream
(Audio A+C+D)
MediaStream
(Audio A+B+D)
MediaStream
(Audio A+B+C)
VideoのみのMediaStreamと
AudioのみのMediaStreamの
Multi-Stream
ブラウザMCU DEMO 2
• Canvasの自由度は凄い!
20
ブラウザMCU DEMO 3
• Canvas + WebGL で3Dも行ける!
• 8人 (今回は録画で)
• ~25人(今回は録画で)
– さすがに厳しい…
21
ブラウザMCU DEMO 4
22
• MediaRecorderで録画もできる!
MCUサーバー役の仕組み:録画
23
MediaStream
(Video A+B+C+D)
<canvas>タグ 配信用のものを利用
Web Audio API
MediaStream
(Audio A+B+C+D)
録画用に改めて準備
videoTracks[] MediaStreamTrack
MediaStreamTrackaidioTracks[]
MediaStream
新しく生成
MediaStream.addTrack()
で追加
MediaStream.addTrack()
で追加
MediaRecorder
WebM
MCU vs. SFU
24
SFU: Selective Forwarding Unit
25
ブラウザ
A
ブラウザ
B
ブラウザ
D
ブラウザ
C
SFU
映像・音声
を分岐/配信
ブラウザ
A
ブラウザ
B
ブラウザ
D
ブラウザ
C
MCU
映像・音声
を合成
MCUの場合
• MCUサーバ必要 → CPU負荷:激高 ××
• ブラウザ側はCPU/ネットワーク負荷:低 ◎
SFUの場合
• SFUサーバ必要 → CPU負荷:低 ○
• ブラウザ側はCPU負荷:低め ○
• ブラウザ側はネットワーク負荷:中 △
表示レイアウトの
自由度が高い ◎
ブラウザMCU DEMO 5
• MCUは表示レイアウトが固定 → 自由度が低い
• ... 否、無理やりレイアウト変更もできる!
26
オマケ:MCUの泣き所
• 本物のMCUでは、タイミング合わせが大変
– それぞれの映像のコマのタイミングを合わせる
– 映像と音声のタイミングを合わせる
– → 今回のブラウザMCUでは、一切タイミング合わせはしない
• タイミング合わせのために、ある程度待つことも必要
– ただし、いつまでも待っていてはならない
– クライアント側との通信が切断されたら、あきらめる必要がある
• 黒コマを送る、or 最後の絵を送る、など
– → 今回のブラウザMCUでは、何も気にしなくて良い
27
DEMO
まとめ
• WebRTC + Canvas の可能性は無限大
– とても楽しいオモチャ
• ローカルファイルの配信も可能
• http://qiita.com/massie_g/items/5a6c4b69374d5997dc37
• とは言え、サーバ役にムチャクチャ負荷がかかる
– スケールしない、非実用的
• ユーザにサーバ役も賄ってもらえれば、使いようはあるかも?
• 本日のプレゼン資料は Slideshareに
28
Thank you!
29

WebRTC Build MCU on browser