教程版本V6.10.0
server.js代碼如下
var path = require('path');
var url = require('url');
var express = require('express');
var ws = require('ws');
var fs = require('fs');
var https = require('https');
var events = require('events');
var ws_uri = 'ws://localhost:8888/kurento';
var options =
{
key: fs.readFileSync('keys/server.key'),
cert: fs.readFileSync('keys/server.crt')
};
var app = express();
/*
web服務(wù)器
*/
var port = 8443;
var server = https.createServer(options, app).listen(port, function() {
console.log('Kurento Tutorial started');
console.log('Open https://localhost:8444/ with a WebRTC capable browser');
});
var wss = new ws.Server({
server : server,
path : '/one2many'
});
function UserSession() {
var sessionId = null;
var mediaPipeline = null;
var sdpOffer = null;
var WebRtcEndpointId = null;
var ws = null;
var candidatesQueue = null;
var connId = null;
}
var presenterUser = new UserSession();
var viewerUser = new UserSession();
var idCounter = 0;
function nextUniqueId() {
idCounter++;
return idCounter.toString();
}
/*
媒體服務(wù)器控制代碼
*/
var wsk = new ws.connect(ws_uri, function() {
console.log('Connection to server kurento');
self = this;
wsk.on('message', function(_message) {
var message = JSON.parse(_message);
console.log('Connection received message Server :', _message);
switch(message.id) {
case 11:
presenterUser.mediaPipeline = message.result.value;
presenterUser.sessionId = message.result.sessionId;
wsk.createWebRtcEndpoint(presenterUser.sessionId, presenterUser.mediaPipeline);
break;
case 12:
presenterUser.ws.id = message.result.value;
presenterUser.WebRtcEndpointId = message.result.value;
if (presenterUser.candidatesQueue) {
while(presenterUser.candidatesQueue.length) {
var candidate = presenterUser.candidatesQueue.shift();
wsk.invokeAddIceCandidate(presenterUser.sessionId, presenterUser.WebRtcEndpointId, candidate);
}
}
wsk.subscribeCandidate(presenterUser.sessionId, presenterUser.WebRtcEndpointId);
wsk.invokeProcessOffer(presenterUser.sessionId, presenterUser.WebRtcEndpointId, presenterUser.sdpOffer);
wsk.invokeGatherCandidates(presenterUser.sessionId, presenterUser.WebRtcEndpointId);
break;
case 14:
presenterUser.ws.send(JSON.stringify({
"id": "presenterResponse",
"response": "accepted",
"sdpAnswer": message.result.value
}));
break;
case 21:
break;
case 22:
viewerUser.ws.id = message.result.value;
viewerUser.WebRtcEndpointId = message.result.value;
if (viewerUser.candidatesQueue) {
while(viewerUser.candidatesQueue.length) {
var candidate = viewerUser.candidatesQueue.shift();
wsk.invokeAddIceCandidate(viewerUser.sessionId, viewerUser.WebRtcEndpointId, candidate);
}
}
wsk.subscribeCandidate(viewerUser.sessionId, viewerUser.WebRtcEndpointId);
wsk.invokeProcessOffer(viewerUser.sessionId, viewerUser.WebRtcEndpointId, viewerUser.sdpOffer);
wsk.invokeConnect(viewerUser.sessionId, viewerUser.WebRtcEndpointId, presenterUser.WebRtcEndpointId);
break;
case 23:
wsk.invokeGatherCandidates(viewerUser.sessionId, viewerUser.WebRtcEndpointId);
break;
case 24:
viewerUser.ws.send(JSON.stringify({
"id": "viewerResponse",
"response": "accepted",
"sdpAnswer": message.result.value
}));
break;
}
if (message.method == 'onEvent' && message.params.value.type == 'OnIceCandidate') {
if (idCounter == presenterUser.connId) {
wsk.invokeAddIceCandidate(presenterUser.sessionId, presenterUser.WebRtcEndpointId, message.params.value.data.candidate);
}
}
});
wsk.createMediaPipeline = function() {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 11 : 21,
"method": "create",
"params": {
"type": "MediaPipeline",
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> createMediaPipeline:', message);
wsk.send(message);
}
wsk.createWebRtcEndpoint = function(_sessionId, _mediaPipeline) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 12 : 22,
"method": "create",
"params": {
"type": "WebRtcEndpoint",
"constructorParams": {"mediaPipeline": _mediaPipeline},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> createWebRtcEndpoint:', message);
wsk.send(message);
}
wsk.invokeConnect = function(_sessionId, _sink, _object) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 13 : 23,
"method": "invoke",
"params": {
"object": _object,
"operation": "connect",
"operationParams": {"sink": _sink},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> invokeConnect:', message);
wsk.send(message);
}
wsk.invokeProcessOffer = function(_sessionId, _WebRtcEndpointId, _sdpOffer) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 14 : 24,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "processOffer",
"operationParams": {"offer": _sdpOffer},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> invokeProcessOffer:', message);
wsk.send(message);
}
wsk.subscribeCandidate = function(_sessionId, _WebRtcEndpointId) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 15 : 25,
"method": "subscribe",
"params": {
"object": _WebRtcEndpointId,
"type": "OnIceCandidate",
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> subscribeCandidate:', message);
wsk.send(message);
}
wsk.invokeGatherCandidates = function(_sessionId, _WebRtcEndpointId) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 16 : 26,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "gatherCandidates",
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> invokeGatherCandidates:', message);
wsk.send(message);
}
wsk.invokeAddIceCandidate = function(_sessionId, _object, _candidate) {
var message = JSON.stringify({
"id": idCounter == presenterUser.connId ? 17 : 27,
"method": "invoke",
"params": {
"object": _object,
"operation": "addIceCandidate",
"operationParams": {"candidate": _candidate},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
console.log('Kurento Media Server -> invokeAddIceCandidate:', message);
wsk.send(message);
};
});
/*
客戶端響應(yīng)代碼
*/
wss.on('connection', function(ws) {
nextUniqueId();
console.log('Connection received with :' + idCounter);
ws.on('error', function(error) {
console.log('Connection error');
});
ws.on('close', function() {
console.log('Connection closed');
});
ws.on('message', function(_message) {
var message = JSON.parse(_message);
console.log('Connection received message ', message);
switch (message.id) {
case 'presenter':
presenter(message.sdpOffer, ws);
break;
case 'viewer':
viewer(message.sdpOffer, ws);
break;
case 'onIceCandidate':
if (idCounter == presenterUser.connId) {
onIceCandidate(message.candidate, presenterUser);
} else {
onIceCandidate(message.candidate, viewerUser);
}
break;
default:
ws.send(JSON.stringify({
id : 'error',
message : 'Invalid message ' + message
}));
break;
}
});
ws.on('OnIceCandidate', function(event) {
console.log("get OnIceCandidate:", event.candidate);
ws.send(JSON.stringify({
"id": "iceCandidate",
"candidate": event.candidate
}));
});
});
function presenter(_sdpOffer, _ws) {
presenterUser.sdpOffer = _sdpOffer;
presenterUser.ws = _ws;
presenterUser.connId = idCounter;
wsk.createMediaPipeline();
}
function viewer(_sdpOffer, _ws) {
viewerUser.sdpOffer = _sdpOffer;
viewerUser.ws = _ws;
viewerUser.sessionId = presenterUser.sessionId;
viewerUser.mediaPipeline = presenterUser.mediaPipeline
wsk.createWebRtcEndpoint(viewerUser.sessionId, viewerUser.mediaPipeline);
}
function onIceCandidate(_candidate, _user) {
if (_user.WebRtcEndpointId == null) {
if (_user.candidatesQueue == null) {
_user.candidatesQueue = [];
}
_user.candidatesQueue.push(_candidate);
} else {
wsk.invokeAddIceCandidate(_user.sessionId, _user.WebRtcEndpointId, _candidate);
}
}
app.use(express.static(path.join(__dirname, 'static')));
與Kurento Media Server交互
表演者
1.創(chuàng)建媒體管道
發(fā)送
{
?"id": "$id",
?"method": "create",
?"params": {
??"type": "MediaPipeline"
?},
?"jsonrpc": "2.0"
}
返回
{
?"id": "$id",
?"jsonrpc": "2.0",
?"result": {
??"sessionId": "$sessionId",
??"value": "$mediaPipeline"
?}
}
2.創(chuàng)建webrtc節(jié)點(diǎn)
發(fā)送
{
?"id": "$id",
?"method": "create",
?"params": {
??"type": "WebRtcEndpoint",
??"constructorParams": {
???"mediaPipeline": "$mediaPipeline"
??},
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回
{
?"id": "$id",
?"jsonrpc": "2.0",
?"result": {
??"sessionId": "$sessionId",
??"value": "$WebRtcEndpoint"
?}
}
3.將客戶端發(fā)送過來的candidate傳輸給服務(wù)器
發(fā)送
{
?"id": "$id",
?"method": "invoke",
?"params": {
??"object": "$WebRtcEndpoint",
??"operation": "addIceCandidate",
??"operationParams": {
???"candidate": $clientCandidate
??},
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回(可以判斷是否成功)
4.訂閱并獲取服務(wù)器的candidate钉嘹,回傳至服務(wù)器
訂閱服務(wù)器的candidate
發(fā)送
{
?"id": "$id",
?"method": "subscribe",
?"params": {
??"object": "$WebRtcEndpoint",
??"type": "OnIceCandidate",
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回(可以判斷是否成功)
獲取服務(wù)器的candidate
發(fā)送
{
?"id": "$id",
?"method": "invoke",
?"params": {
??"object": "$WebRtcEndpoint",
??"operation": "gatherCandidates",
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回(可以判斷是否成功)
服務(wù)器會(huì)觸發(fā)message事件得到
{
?"jsonrpc": "2.0",
?"method": "onEvent",
?"params": {
??"value": {
???"data": {
????"candidate": "$serverCandidate",
????...
???},
???...
??}
?}
}
回傳至服務(wù)器
發(fā)送
{
?"id": "$id",
?"method": "invoke",
?"params": {
??"object": "$WebRtcEndpoint",
??"operation": "addIceCandidate",
??"operationParams": {
???"candidate": "$serverCandidate"
??},
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回(可以判斷是否成功)
4.交換sdp信息
將客戶端的sdp發(fā)送給服務(wù)器
發(fā)送
{
?"id": "$id",
?"method": "invoke",
?"params": {
??"object": "$WebRtcEndpoint",
??"operation": "processOffer",
??"operationParams": {
???"offer": "$clientSdpOffer"
??},
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回
{
?"id": 14,
?"jsonrpc": "2.0",
?"result": {
??"sessionId": "$sessionId",
??"value": "$serverSdpOffer"
?}
}
服務(wù)器返回sdp回傳給客戶端
將serverSdpOffer傳給客戶端
觀察者
媒體管道與連接會(huì)話使用表演者的
1.創(chuàng)建webrtc節(jié)點(diǎn)
同表演者一樣
2.將客戶端發(fā)送過來的candidate傳輸給服務(wù)器
同表演者一樣
3.訂閱并獲取服務(wù)器的candidate(不需要回傳至服務(wù)器)
訂閱服務(wù)器的candidate
同表演者一樣
獲取服務(wù)器的candidate
同表演者一樣
4.交換sdp信息
同表演者一樣
5.連接表演者
發(fā)送
{
?"id": "$id",
?"method": "invoke",
?"params": {
??"object": "$WebRtcEndpoint",?// 表演者
??"operation": "connect",
??"operationParams": {
???"sink": "$WebRtcEndpoint"?// 觀察者
??},
??"sessionId": "$sessionId"
?},
?"jsonrpc": "2.0"
}
返回(可以判斷是否成功)