Top Banner
Node学園祭 2014 Nodeで操るKurentoメディアサーバ 2014/11/15 発表:がねこまさし 全面協力:ばばあつし
97

Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Jul 04, 2015

Download

Technology

mganeko

Node学園祭2014での発表資料です。
Kurento + WebRTC + Node.js の話をします。
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Node学園祭 2014Nodeで操るKurentoメディアサーバ

2014/11/15発表:がねこまさし

全面協力:ばばあつし

Page 2: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

はじめに

• 本日の内容は、こちらの内容を全面的に参考にしています

– http://www.kurento.org/docs/current/

• 本利用に含まれる製品名、商標、ロゴは、各権利者に帰属します。

– ®、TM 等の表記は省略します

Page 3: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

自己紹介

• がねこまさし @massie_g– インフォコム株式会社 所属

• Node学園祭2013に初登場– WebRTCの話をさせていただきました– Node.jsの話はちょっとだけ– http://www.slideshare.net/mganeko/2013-web-rtcnode

• HTML5Experts.jp– WebRTCの話を書かせていただいてます– http://html5experts.jp/mganeko/

• WebRTC Meetup Japan– いまのところ毎回しゃべらせていただいてます– http://www.slideshare.net/mganeko/meetup1-webrtc– ※次回は 2014/11/26(水) https://atnd.org/events/58755

Page 4: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

今日の話

Node.js

WebRTCメディア

サーバー

Page 5: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

今日の話

Node.js

WebRTCメディア

サーバー

ここ!(とっても狭い)

Page 6: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

今日の話

Node.js

WebRTCメディア

サーバー

ここ!(とっても狭い)

みなさん、大丈夫ですか?部屋間違ってませんか?

Page 7: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

WebRTC & Node.js おさらい

Page 8: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

質問タイム

• (1) WebRTC という言葉を聞いたことがある人は?

• (2) WebRTCのサンプルに触ったことがある人は?

• (3) WebRTCを使ったコードを書いたことがある人は?

8

Page 9: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

WebRTCの構成要素

• ユーザーメディア getUserMedia()– カメラ– マイク– 画面キャプチャ

• ストリーム (MediaStream)

• P2P通信 (RTCPeerConnection)• データ通信 (DataChannel)

• 関連するAPI、HTML要素– JavaScript (大前提)– Video, Audio– WebScoket– WebAudio API– Canvas– WebGL

9

Page 10: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

通信について

• RTCPeerConnection

– 動画、音声などのMediaStreamを転送する経路

– P2P (Peer to Peer) → ブラウザとブラウザ

– UDP/IP を使用

10

RTCPeerConnection RTCPeerConnection

UDP/IP

Page 11: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

P2P通信を始めるには• お互い、相手のIPアドレスを知る必要がある• 使用するポート番号を知る必要がある

– 利用するUDPのポートはダイナミックに割り振られる

• RTCPeerConnectionの通信を始める前に、何らかの手段でネゴシエーション、合意が必要– この手順を”シグナリング:Signaling”と呼びます

11

RTCPeerConnection RTCPeerConnection

UDP/IP

お互いのIPアドレス利用するUDPポート

Page 12: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

P2P開始前のシグナリング

• どちらのブラウザからもわかる、中継役が必要– →普通はシグナリングサーバーを立てる

• シグナリングのプロトコルは標準化されていない– 独自の方式

• WebSocket利用(TCP/IP)• Ajax利用(HTTP, HTTPS)

– 既存のプロトコル• SIP(VoIP用) with WebSocket(TCP/IP)• XMPP(IM用)with WebSocket(TCP/IP)

12

(1) 情報交換(シグナリング)

(2) P2P

Page 13: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

P2P開始前のシグナリング

• どちらのブラウザからもわかる、中継役が必要– →普通はシグナリングサーバーを立てる

• シグナリングのプロトコルは標準化されていない– 独自の方式

• WebSocket利用(TCP/IP)• Ajax利用(HTTP, HTTPS)

– 既存のプロトコル• SIP(VoIP用) with WebSocket(TCP/IP)• XMPP(IM用)with WebSocket(TCP/IP)

13

Node.js+ socket.io

Page 14: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

DEMO

• 通信してみます

– インターネットにつながったら

Page 15: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Peer-to-Peer

• 良いところ

– 通信のオーバーヘッドが少ない

• 悪いところ

– 通信相手が多いと、マシンの負荷が高くなる

– ネットワーク負荷

– 動画エンコード負荷

• 相手ごとに圧縮している

Page 16: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

多人数での利用の実現

メディアサーバ活用

Page 17: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

なぜメディアサーバを使うのか

• なしの場合、同時接続数に限界あり(≒5端末)

• メディアサーバを使うと、負荷がサーバ側に移動する

• → (それなりのサーバを使えば)より多人数での接続が可能

Media Server

メディアサーバーありの構成

H/W・N/W負荷小

P2Pの構成

H/W・N/W負荷大

Page 18: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

MCUとメディアサーバー

• MCU

– 多地点接続装置 / Multi point Control Unit

• メディアサーバー

MCUの機能に加えて

– メディア変換

– 録画、再生

などの機能を持つことが多い(みたい)

Page 19: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

MCUとメディアサーバー

• MCU

– 多地点接続装置 / Multi point Control Unit

• メディアサーバー

MCUの機能に加えて

– メディア変換

– 録画、再生

などの機能を持つことが多い(みたい)

Page 20: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

What’s Kurento?

http://www.kurento.org/

Page 21: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurentoとはエスペラント語で「Stream」の意味

カレントと発音するようです。

2013年頃から開発が始まり、現在は Ver5.0

が発表されています。

・2014 WebRTC EXPO IV Pioneer Awards

most innovative technologies受賞

・GStreamer Conferences 2014 Speaker

Page 22: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

KurentoとはWebRTCをサポートしたメディアサーバ

基本的なMCU/トランスコードの他 Mixing

AR等のメディア加工がリアルタイムに可能

Page 23: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurentoの構造

GStreamerhttp://gstreamer.freedesktop.org/

C言語で作られた、メディアエンジン

Kurento Media Server http://www.kurento.org/

C++で作られた、メディアサーバー

Kurento Client for Java

Kurento Client for JS

Kurento Client for Node.js

JSON RPC over ws

Page 24: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

システムの構成

Page 25: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento提供の部品

Page 26: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

部品を組合せて利用

Page 27: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Why Kurento?

Page 28: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

なぜ選んだか• 変換不要でWebRTCと直結できる。

– 映像を合成、通信を1本化できる

• フリー(OSS) GNU LGPL 2.1ライセンス

• Node対応のAPIが提供されている– 既存のシグナリングサーバと同じ環境にしたい

• TURNにも対応– NAT超えの環境に対応

• GStreamerがベース– プラグイン豊富で開発が活発

Page 29: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Use Kurento

Page 30: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

> sudo service kurento-media-server start

> sudo add-apt-repository ppa:kurento/kurento> wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add –> sudo apt-get update> sudo apt-get install kurento-media-server

セットアップhttp://www.kurento.org/docs/current/installation_guide.htmlサーバはUbuntu 14.04 LTS (64 bits)奨励

サーバインストール

設定ファイル/etc/kurento/kurento.conf.json ⇒必要に応じてTURN/STUNの設定を追加/etc/kurento/sdp_pattern.txt ⇒必要ならSDPのひな形を編集

サーバ起動

Page 31: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

kurento.conf.json 例{

"mediaServer" : {"net" : {

"websocket": {"port": 8888,"path": "kurento","threads": 10

}}

},"modules": {

"kurento": {"SdpEndpoint" : {

"sdpPattern" : "sdp_pattern.txt"},"WebRtcEndpoint" : {

"stunServerAddress" : "111.112.113.114", // Only IP address are supported"stunServerPort" : 80,"turnURL" : "user:[email protected]:80?transport=tcp"

},

JSON-RPC over ws の待ち受けポート、URL等

SDPのひな形

STUN/TURNサーバーの指定

同時に接続できる数

Page 32: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

sdp_pattern.txt 例

v=0o=- 0 0 IN IP4 0.0.0.0s=TestSessionc=IN IP4 0.0.0.0t=0 0m=audio 0 RTP/AVP 98 99 0a=rtpmap:98 OPUS/48000/1a=rtpmap:99 AMR/8000/1a=rtpmap:0 PCMU/8000m=video 0 RTP/AVP 95 96 100a=rtpmap:95 VP8/90000a=rtpmap:96 H263-1998/90000a=rtpmap:100 MP4V-ES/90000

Page 33: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

SDPって何?

• Session Description Protocol

– VoIP等で利用されている

• ストリームの情報、Peerの情報を含む

– メディアの種類(音声、映像)、コーデック

– IPアドレス、ポート番号

– P2Pのデータ転送プロトコル → Secure RTP

– 通信で使用する帯域

など

Page 34: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

> sudo npm install -g bower> bower install kurento-client> bower install kurento-util

> sudo add-apt-repository ppa:chris-lea/node.js> sudo npm install express> sudo npm install kurento-client> sudo npm install kurento-util

Nodeで使うには (A)Webサーバ&シグナリングサーバー

シグナリングサーバにAPIを追加

package.jsonを使って npm install でもOK

bower.jsonを使って bower installでもOK

以上で準備できました。

Page 35: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

> sudo npm install -g bower> bower install kurento-util> cp bower_components/kurento-utils/js/kurento-utils.js 適切なWebサーバー

> sudo add-apt-repository ppa:chris-lea/node.js> sudo npm install socket.io> sudo npm install kurento-client

Nodeで使うには (B)シグナリングサーバーのみ (Webサーバ別)

シグナリングサーバにAPIを追加

以上で準備できました。

Webサーバーにブラウザ用JavaScriptを追加

Page 36: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックデモ

• メディアサーバ経由でループバック

– ブラウザからの映像を、そのまま送り返す

Kurento Media Server自分の映像/音声を送信

メディアサーバ側でループバック

Page 37: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:準備// -- kurento client ---var kurento = require('kurento-client');var kurentoClient = null;const ws_uri = 'ws://kurento.sample.com:8888/kurento';

function prepareKurento() {kurento(ws_uri, function(error, client) {

if (error) { return onError(error); }

kurentoClient = client;});

}

prepareKurent();

// -- create the socket server on the port ---var srv = require('http').Server();var io = require('socket.io')(srv);var port = 8000;srv.listen(port);io.on('connection', function(socket) {// …

}

Socket.IO サーバー

Kurentoクライアント

Page 38: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームループバックの流れシグナリングサーバー Kurento Media Server

kurentoClient

ブラウザ

Page 39: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:ビデオ開始、通信要求

var pc_config = {“iceServers”:[ // ← TURN設定{"url":"turn:turn.xxxx.xxxx:80?transport=udp", "username":“xxxxx", "credential":“xxxxx"},{"url":"turn:turn.xxxx.xxxx:80?transport=tcp", "username":“xxxxx", "credential":“xxxxx"}

]};kurentoUtils.WebRtcPeer.prototype.server = pc_config;var webRtcPeer = null;var localVideo = document.getElementById('local_video');var remoteVideo = document.getElementById('remote_video');var socket = io.connect(‘http://シグナリングサーバー:ポート/');function startVideo() {

webRtcPeer = kurentoUtils.WebRtcPeer.startSendRecv( // ← クライアント側WebRTC Peer作成localVideo, remoteVideo, onOfferCreated,onError

);}

function onOfferCreated(sdpOffer) {var msg = "echoback_request";socket.emit(msg, sdpOffer); // ← サーバへOffer SDP送信

}

<script src=“js/adapter.js”></script> <!-- ブラウザ互換スクリプト --><script src="js/kurento-utils.js"></script>

Page 40: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームループバックの流れシグナリングサーバー Kurento Media Server

webrtcPeer

kurentoClient

ブラウザ

Page 41: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックの流れブラウザ シグナリングサーバー Kurento Media Server

connect

kurentClient

Offer SDP

Page 42: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:映像処理開始io.on('connection', function(socket) {// …socket.on('echoback_request', function(sdp) {

handleEchobackOffer(sdp, socket);});

}

var media_pipeline = null;function handleEchobackOffer(sdp, socket) { //← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return null;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { //← サーバ側のWebRTC作成webRtcEndpoint.connect(webRtcEndpoint, function(error) { // ← Loopするように繋ぐwebRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

//← サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer }); //Answer用SDPをWebSocketでクライアントへ送信

});});

});}

Page 43: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームループバックの流れシグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

PipelinekurentoClient

ブラウザ

Page 44: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:映像処理開始io.on('connection', function(socket) {// …socket.on('echoback_request', function(sdp) {

handleEchobackOffer(sdp, socket);});

}

var media_pipeline = null;function handleEchobackOffer(sdp, socket) { //← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { //← サーバ側のWebRTC作成webRtcEndpoint.connect(webRtcEndpoint, function(error) { // ← Loopするように繋ぐwebRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

//← サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer }); //Answer用SDPをWebSocketでクライアントへ送信

});});

});}

Page 45: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームループバックの流れシグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

WebRtcEndpointwebrtcEndpoint

kurentoClient

ブラウザ

Page 46: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:映像処理開始io.on('connection', function(socket) {// …socket.on('echoback_request', function(sdp) {

handleEchobackOffer(sdp, socket);});

}

var media_pipeline = null;function handleEchobackOffer(sdp, socket) { //← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return null;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { //← サーバ側のWebRTC作成webRtcEndpoint.connect(webRtcEndpoint, function(error) { // ← Loopするように繋ぐwebRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

//← サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer }); //Answer用SDPをWebSocketでクライアントへ送信

});});

});}

Page 47: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームループバックの流れシグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

WebRtcEndpointwebrtcEndpoint

kurentoClient

ブラウザ

Page 48: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:映像処理開始io.on('connection', function(socket) {// …socket.on('echoback_request', function(sdp) {

handleEchobackOffer(sdp, socket);});

}

var media_pipeline = null;function handleEchobackOffer(sdp, socket) { //← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return null;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { //← サーバ側のWebRTC作成webRtcEndpoint.connect(webRtcEndpoint, function(error) { // ← Loopするように繋ぐwebRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

//← サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer }); //Answer用SDPをWebSocketでクライアントへ送信

});});

});}

Page 49: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバー側:応答を返すio.on('connection', function(socket) {// …socket.on('echoback_request', function(sdp) {

handleEchobackOffer(sdp, socket);});

}

var media_pipeline = null;function handleEchobackOffer(sdp, socket) { //← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return null;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { //← サーバ側のWebRTC作成webRtcEndpoint.connect(webRtcEndpoint, function(error) { // ← Loopするように繋ぐwebRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

//← サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer }); //Answer用SDPをWebSocketでクライアントへ送信

});});

});}

Page 50: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックの流れブラウザ シグナリングサーバー Kurento Media Server

connect

kurentClient

Offer SDPceate “Pipeline”

pipeline

ceate “WebRtcEndpoint”

WebRtcEndpoint

set Offer

Answer SDPAnswer SDP

Page 51: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:応答(Answer)を受け取る

var socket = io.connect(‘シグナリングサーバーURL’);socket.on('connect', onChannelOpened).on('echoback_response', onEchobackResponse)

function onEchobackResponse(evt) {if (evt.sdp) {webRtcPeer.processSdpAnswer(evt.sdp); // Answer SDP を受け付ける}

}

Page 52: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックの流れ:全体シグナリングサーバー

pipeline

webrtcPeer

Pipeline

WebRtcEndpointwebrtcEndpoint

kurentoClient

ブラウザ

対応生成

ストリーム

Kurento Media Server

Page 53: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックの流れブラウザ シグナリングサーバー Kurento Media Server

connect

kurentClient

Offer SDPceate “Pipeline”

pipeline

ceate “WebRtcEndpoint”

WebRtcEndpoint

set Offer

Answer SDPAnswer SDP

Peer to Peer 通信開始

Page 54: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

補足:ICE Candidateについて

• シグナリングの過程では、2種類の情報をやりとりします

– SDP (Peerの情報)

– ICE Candidate (通信経路の情報)

• Kurento では、 SDP+ICEをまとめて、一度に送っています

Page 55: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

TURNサーバーが有る場合:全体シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

WebRtcEndpointwebrtcEndpoint

kurentoClient

ブラウザ

TURNサーバー

firewall

Page 56: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:停止function stopVideo() {

if (webRtcPeer) {webRtcPeer.dispose();webRtcpeer = null;}

}

Page 57: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ループバックデモ 2• メディアサーバで映像を加工

– ブラウザからの映像を、加工してから送り返す

Kurento Media Server自分の映像/音声を送信

メディアサーバ側で映像加工(Filter)してループバック

Page 58: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:映像開始、フィルター指定

var webRtcPeer = null; var localVideo = document.getElementById('local_video');var remoteVideo = document.getElementById('remote_video');

var webRtcPeer = null;function startVideo() {

webRtcPeer = kurentoUtils.WebRtcPeer.startSendRecv( //← クライアント側WebRTC Peer作成localVideo, remoteVideo, onOfferCreated,onError);

}

function onOfferCreated(sdpOffer) {var msg = "echoback_request";var effect = getEffect();socket.emit(msg, sdpOffer, effect); // ← サーバへSDP送信

}

Page 59: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

サーバー側:フィルター指定Var media_pipeline = null;function handleEchobackOffer(sdp, socket, effect) { // ← ブラウザのWebRTCのSDPをWebSocketで受信kurentoClient.create(‘MediaPipeline’, function(error, pipeline) { ← パイプライン作成if (error) { onError(error); return null;}media_pipeline = pipeline;

pipeline.create(‘WebRtcEndpoint’, function(error, webRtcEndpoint) { // ← サーバ側のWebRTC作成// 加工フィルター作成pipeline.create(‘GStreamerFilter’,{command : effect,filterType:“VIDEO”}, function(error, filter) {webRtcEndpoint.connect(filter, function(error) { // WebRtcEndpoint → フィルタに繋ぐfilter.connect(webRtcEndpoint, function(error) { // フィルター → WebRtcEndpointに繋ぐ// サーバ側のWebRTC Answer用SDP作成webRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

// サーバ側のWebRTC Answer用SDP作成socket.emit(‘echoback_response’, { sdp: sdpAnswer });

});});});});

});}

Page 60: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリームフィルター有りの流れ:全体シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

WebRtcEndpointwebrtcEndpoint

kurentoClient

ブラウザ

GStreamerFiltergstreamerFilter

Page 61: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

多人数会議のデモ

• N対Nの通信

– N人分の映像を合成

– N-1人分の音声を合成

• 自分は含まない

– 下りのストリームを1本化

Page 62: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server自分の映像/音声を送信

合成された映像/音声を送信

入力ソース3つを1つに合成

映像×3➡1音声×2 ➡1

Kurento Media Server デモ N対N 映像/音声

Page 63: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム多人数合成の流れ:全体シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcEndPoint

webrtcEndPoint

webrtcEndPoint

hubporthubport

hubport

Page 64: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:入室、映像開始var socket = io.connect(‘シグナリングサーバーのURL');socket.on('connect', onChannelOpened).on('multi_response', onPlaybackResponse)

function onChannelOpened(evt) {var roomname = getRoomName();socket.emit(‘enter’, roomname); // 会議室に入る

}

var webRtcPeer = null;function startVideo() { // 映像開始

webRtcPeer = kurentoUtils.WebRtcPeer.startSendRecv(localVideo, remoteVideo,onOfferCreated,onError

);}

function onOfferCreated(sdpOffer) {var room = getRoomName();var msg = "multi_start";socket.emit(msg, room, sdpOffer); // Offer SDP を送信

}

Page 65: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:入室

socket.on('enter', function(roomname) {socket.join(roomname);setRoomname(roomname);

setupRoom(roomname, socket);});

function setRoomname(room) {//// for v0.9//socket.set('roomname', room);

// for v1.0socket.roomname = room;

}

Page 66: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:部屋の準備// 部屋ごとvar media_pipelines = [];var composites = [];

function setupRoom(roomname, socket) {if (media_pipelines[roomname]) {

// 部屋の準備は完了notifyRoomReady(roomname, socket);

}

// まだ部屋に対応した要素が無ければ、作成kurentoClient.create('MediaPipeline', function(error, pipeline) {

media_pipelines[roomname] = pipeline;pipeline.create("Composite", function(error, comp, socket) {composites[roomname] = comp;notifyRoomReady(roomname, socket);

});});

}

function notifyRoomReady(roomname, socket) {socket.emit('room_ready', {roomname: roomname} );

}

Page 67: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム多人数合成の流れ:部屋の準備

シグナリングサーバー Kurento Media Server

pipeline

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcPeer

Page 68: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:接続開始// --- クライアントごと ---var webrtcs = [];var hubs = [];

socket.on('multi_start', function(room, sdp) {handleOffer(sdp, room, socket);

});

function handleOffer(sdp, roomname, socket) {var pipeline = media_pipelines[roomname];var composite = composites[roomname];pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) {

webrtcs[socket.id] = webRtcEndpoint;composite.createHubPort(function(error, hubpoint) {hubs[socket.id] = hubpoint;hubpoint.connect(webRtcEndpoint);webRtcEndpoint.connect(hubpoint);webRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {

socket.emit('multi_response', { sdp: sdpAnswer, endpoit: webRtcEndpoint.id });});});

});}

Page 69: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

多人数合成の流れ:1人目シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcEndPoint

hubport

Page 70: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:映像受信

var socket = io.connect(‘シグナリングサーバーURL’);socket.on('connect', onChannelOpened).on(‘multi_response', onMultiResponse)

function onMultiResponse(evt) {if (evt.sdp) {webRtcPeer.processSdpAnswer(evt.sdp); // Answer SDP を受け付ける}

}

Page 71: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム多人数合成の流れ:全体シグナリングサーバー Kurento Media Server

pipeline

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcEndPoint

webrtcEndPoint

webrtcEndPoint

hubporthubport

hubport

ブラウザブラウザ

webrtcPeer

Page 72: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:おまけ

// 部屋に接続しているクライアントの数function getClientCount(room) {

//// for v0.9//var clients = io.sockets.clients(room);//var count = clients.length;

// for v1.0var namespace = '/';var count = 0;var roominfo = io.nsps[namespace].adapter.rooms[room];if (roominfo) {

count = Object.keys(roominfo).length;}return count;

}

もっと良い方法があったら、教えてください!

Page 73: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server

録画

再生

Kurentoを使った録画、再生の基本形

Page 74: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

複雑な録画、再生のデモ

• N対Nの通信で

– N人分の映像を合成

– N-1人分の音声を合成

– 下りのストリームを1本化

• それを録画、再生

Page 75: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server自分の映像/音声を送信

合成された映像/音声を送信

録画

Kurento Media Server デモ 複雑な録画、再生

再生

Page 76: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム多人数録画シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcEndPoint

hubport

recorderEndPoint

Page 77: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:録画開始、停止var recorders = [];function startRecord(room, socket_id) {var pipeline = media_pipelines[room];var file_uri = 'file:///tmp/' + room + '_' + socket_id + '_mix.webm';var hub = hubs[id];pipeline.create('RecorderEndpoint', { uri: file_uri }, function(error, recorder) {

if (error) { onError(error); return; }

recorders[socket_id] = recorder;hub.connect(recorder);recorder.record();

});}

function stopRecord(socket_id) {var recorder = recorders[socket_id];if (recorder) {

recorder.stop();return;

}}

Page 78: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム

Pipeline

再生シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

kurentoClient

ブラウザ

webrtcEndPoint

playerEndPoint

webrtcPeerwebrtcEndPoint

pipeline

Page 79: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:再生開始function playbackStart(room, sdpOffer, socket) {var file_uri = 'file:///tmp/' + room + '_' + socket_id + '_mix.webm';kurentoClient.create('MediaPipeline', function(error, pipeline) {

pipeline.create('WebRtcEndpoint', function(error, webRtc) {webRtc.processOffer(sdpOffer, function(error, sdpAnswer) {

pipeline.create("PlayerEndpoint", { uri: file_uri }, function(error, player) {socket.emit('playback_response', { id: socket.id, sdp: sdpAnswer, playerpoint: player.id });player.on('EndOfStream', function(event) {pipeline.release();console.log('End Playing');

});player.connect(webRtc, function(error) {

player.play(function(error) {console.log("Playing ...");

});});

});});

});});

}

Page 80: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server

Kurentoで多人数配信の基本形

Page 81: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

複雑な多人数配信のデモ

• N対Nの通信で

– N人分の映像を合成

– N-1人分の音声を合成

– 下りのストリームを1本化

• それを多人数に配信

Page 82: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server

Kurento Media Server デモ 複雑な多人数配信

Page 83: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

対応生成

ストリーム

webrtcEndPoint

多人数合成→配信シグナリングサーバー Kurento Media Server

pipeline

webrtcPeer

Pipeline

Composite

kurentoClient

ブラウザ

Composite

webrtcEndPointwebrtcEndPoint

hubporthubport

hubport

webrtcPeerwebrtcEndPoint

話す人

見る人

webrtcPeer

見る人

webrtcEndPoint

Page 84: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

ブラウザ側:配信開始の要求

function startPlayback() {webRtcPeerPlayback = kurentoUtils.WebRtcPeer.startRecvOnly(playbackVideo,onPlaybackOfferCreated,onError

);}

function onPlaybackOfferCreated(offer) {var room = getRoomName();var msg = 'playback_start';socket.emit(msg, room, offer);

}

Page 85: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:配信開始

socket.on('viewer_request', function(room, sdp) {masterid = findFirstSocketIdByRoomId(room); handleViewerOffer(sdp, room, socket, masterid);

});

function handleViewerOffer(sdp, roomname, socket, masterid) {var pipeline = media_pipelines[roomname];var composite = composites[roomname];

pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) {if (error) { onError(error); return; }webrtcs[socket.id] = webRtcEndpoint;hubs[masterid].connect(webRtcEndpoint);

webRtcEndpoint.processOffer(sdp, function(error, sdpAnswer) {if (error) { onError(error); return; }socket.emit('viewer_response', { sdp: sdpAnswer, endpoit: webRtcEndpoint.id });

});});

}

Page 86: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

シグナリングサーバ側:おまけ

// 部屋につながっている最初のソケットIDを取得する

function findFirstSocketIdByRoomId(room) {var res = null;var namespace = '/';roominfo = io.nsps[namespace].adapter.rooms[room];if (roominfo) {

for (var id in roominfo) {res = id;break;

}}return res;

}

もっと良い方法があったら、教えてください!

Page 87: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

パフォーマンス

Page 88: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

他人数配信(基本形)での測定

0

5

10

15

20

25

30

35

40

45

50

1 5 10 15 20 25

CPU使用率

CPU使用率

680

690

700

710

720

730

740

750

760

1 5 10 15 20 25

Memory

Memory

0

500000

1000000

1500000

2000000

2500000

3000000

3500000

1 5 10 15 20 25

recv

send

Network

(人) (人)

(人)

(%)

(Byte/sec)

(MByte)

サーバースペック(ゲストVM)・CPU: 4コア・メモリ: 4GB

・社内ネットワーク・40人まで配信を確認・50人ぐらい行けそうな感触

サーバースペック(ホストマシン)・CPU: 6コア、12スレッド、3.3GHz・メモリ: 64GB

Page 89: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

N対N会議の場合10~12人ぐらいならOK

たくさんの会議をホストしたい場合、複数のKurentoサーバーが必要

Page 90: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

スケールアウトの可能性

Page 91: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

多人数会議×n

会議室ごとに、別のメディアサーバーに接続させる

Page 92: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Kurento Media Server

多人数配信(Multi-Server)

PlumberEndPoint

Kurento Media Server

複数のメディアサーバーを連携して動作させる

Page 93: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

PlumberEndpoint

公式ドキュメントには無いですが、

メディアサーバで処理したストリームを別のメディアサーバと

繋ぐことができる部品が実装されています。

Plumber:配管工

Gstreamer Conference 2014の発表では

輻輳制御で使おうと試みようとしているようでした。

http://gstconf.ubicast.tv/videos/implementing-webrtc-capabilities-for-gstreamer-the-kurento-webrtc-endpoint/

Page 94: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

RabbitMQ Cluster

公式ドキュメントには無いですが、サーバ設定ファイル

にRabbitMQの指定が可能になっています。

Kurento Media Controllerというサーバと組合せて

クラスタリング動作すると思われます。

Page 95: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

感想

Page 96: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

感想

シンプルなAPIでLEGOブロックのように組合せて機能を実現できるのは面白い。

今後部品も増えそうなのでアイディア次第で面白い使い方ができる。

メジャーバージョンアップ(Ver 5.0)でかなりNodeで使いやすくなった。

(それまでは、JBossが必要でした。うへぇ)

使っている人が少ないのか検索してもあまり情報が拾えない。

(ソース解析が主になってます。)

ベースとなっているGStreamerのプラグインが書けるレベルになれば、独自の部品が作成できそう。

大規模に使おうとすると、複数台のサーバーが必要。やっぱお金がかかる….

Page 97: Nodeで操るKurentoメディアサーバー ( Kurento + WebRTC + Node.js )

Thank you!

97