webrtc build mcu on browser
TRANSCRIPT
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
16RTCPeerConnection 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