教程版本V6.10.0
本例子的模型webrtc->KMS->KMS->webrtc
One-To-Many工程地址
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 ws_uri2 = 'ws://10.13.10.71:8888/kurento';
var options =
{
key: fs.readFileSync('keys/server.key'),
cert: fs.readFileSync('keys/server.crt')
};
var app = express();
/*
web服务器
*/
var port = 8443;
var server = https.createServer(options, app).listen(port, function() {
console.log('Kurento Tutorial started');
console.log('Open https://localhost:8443/ 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 candidateCount = null;
var ws = null;
var candidatesQueue = null;
var connId = null;
var events = null;
}
var presenterUser = new UserSession();
var relayUser1 = new UserSession();
var relayUser2 = new UserSession();
var viewerUser = new UserSession();
var idCounter = 0;
function nextUniqueId() {
idCounter++;
return idCounter.toString();
}
/*
媒体服务器控制代码
*/
function createMediaPipeline(_ws, _id) {
var message = JSON.stringify({
"id": _id,
"method": "create",
"params": {
"type": "MediaPipeline",
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function createWebRtcEndpoint(_ws, _id, _sessionId, _mediaPipeline) {
var message = JSON.stringify({
"id": _id,
"method": "create",
"params": {
"type": "WebRtcEndpoint",
"constructorParams": {"mediaPipeline": _mediaPipeline},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function createRtpEndpoint(_ws, _id, _sessionId, _mediaPipeline) {
var message = JSON.stringify({
"id": _id,
"method": "create",
"params": {
"type": "RtpEndpoint",
"constructorParams": {"mediaPipeline": _mediaPipeline},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeConnect(_ws, _id, _sessionId, _object, _sink){
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _object,
"operation": "connect",
"operationParams": {"sink": _sink},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeProcessOffer(_ws, _id, _sessionId, _WebRtcEndpointId, _sdpOffer){
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "processOffer",
"operationParams": {"offer": _sdpOffer},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeProcessAnswer(_ws, _id, _sessionId, _WebRtcEndpointId, _sdpAnswer){
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "processAnswer",
"operationParams": {"answer": _sdpAnswer},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeGenerateOffer(_ws, _id, _sessionId, _WebRtcEndpointId, _sdpOffer) {
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "generateOffer",
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function subscribeCandidate(_ws, _id, _sessionId, _WebRtcEndpointId) {
var message = JSON.stringify({
"id": _id,
"method": "subscribe",
"params": {
"object": _WebRtcEndpointId,
"type": "OnIceCandidate",
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeGatherCandidates(_ws, _id, _sessionId, _WebRtcEndpointId) {
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _WebRtcEndpointId,
"operation": "gatherCandidates",
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
}
function invokeAddIceCandidate(_ws, _id, _sessionId, _object, _candidate) {
var message = JSON.stringify({
"id": _id,
"method": "invoke",
"params": {
"object": _object,
"operation": "addIceCandidate",
"operationParams": {"candidate": _candidate},
"sessionId": _sessionId
},
"jsonrpc": "2.0"
});
_ws.send(message);
return message;
};
var wsk = new ws.connect(ws_uri, function() {
console.log('Connection to server kurento');
var createMediaPipelineFlag = false;
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;
relayUser1.mediaPipeline = presenterUser.mediaPipeline;
relayUser1.sessionId = presenterUser.sessionId;
/* 创建输入端点WebRtcEndpoint 输出端点RtpEndpoint */
console.log("presenterUser createWebRtcEndpoint send:", createWebRtcEndpoint(wsk, 12, presenterUser.sessionId, presenterUser.mediaPipeline));
console.log("relayUser1 createRtpEndpoint send:", createRtpEndpoint(wsk, 29, relayUser1.sessionId, relayUser1.mediaPipeline));
break;
case 12:
presenterUser.events = new events.EventEmitter();
presenterUser.events.on('OnIceCandidate', function(event) {
console.log("presenterUser OnIceCandidate");
});
presenterUser.events.id = message.result.value;
presenterUser.WebRtcEndpointId = message.result.value;
if (presenterUser.candidatesQueue) {
while(presenterUser.candidatesQueue.length) {
var candidate = presenterUser.candidatesQueue.shift();
console.log("presenterUser invokeAddIceCandidate send:", invokeAddIceCandidate(wsk, 17, presenterUser.sessionId, presenterUser.WebRtcEndpointId, candidate));
}
}
console.log("presenterUser subscribeCandidate send:", subscribeCandidate(wsk, 15, presenterUser.sessionId, presenterUser.WebRtcEndpointId));
console.log("presenterUser invokeProcessOffer send:", invokeProcessOffer(wsk, 14, presenterUser.sessionId, presenterUser.WebRtcEndpointId, presenterUser.sdpOffer));
console.log("presenterUser invokeGatherCandidates send:", invokeGatherCandidates(wsk, 16, presenterUser.sessionId, presenterUser.WebRtcEndpointId));
break;
case 14:
var msg = JSON.stringify({
"id": "presenterResponse",
"response": "accepted",
"sdpAnswer": message.result.value
});
console.log("presenterUser ws send:", msg);
presenterUser.ws.send(msg);
break;
case 15:
/* 连接输入输出端点*/
console.log("viewerUser invokeConnect send:", invokeConnect(wsk, 13, presenterUser.sessionId, presenterUser.WebRtcEndpointId, relayUser1.WebRtcEndpointId));
console.log("relayUser1 invokeGenerateOffer send:", invokeGenerateOffer(wsk, 28, relayUser1.sessionId, relayUser1.WebRtcEndpointId));
break;
case 17:
break;
case 28:
relayUser1.sdpOffer = message.result.value;
break;
case 29:
relayUser1.events = new events.EventEmitter();
relayUser1.events.id = message.result.value;
relayUser1.WebRtcEndpointId = message.result.value;
break;
}
if (message.method == 'onEvent' && message.params.value.type == 'OnIceCandidate') {
if (true) {
if (presenterUser.WebRtcEndpointId == message.params.value.data.source) {
var msg = JSON.stringify({
"id": "iceCandidate",
"candidate": message.params.value.data.candidate
});
console.log("presenterUser ws send:", msg);
presenterUser.ws.send(msg);
if (!presenterUser.candidateCount) {
presenterUser.candidateCount = 0;
}
presenterUser.candidateCount = 1 + presenterUser.candidateCount;
if (presenterUser.candidateCount == 24) {
console.log("presenterUser.candidateCount == 24");
if (!createMediaPipelineFlag) {
createMediaPipelineFlag = true;
console.log("relayUser2 createMediaPipeline send:", createMediaPipeline(wsk2, 31));
}
}
}
}
}
});
});
var wsk2 = new ws.connect(ws_uri2, function() {
console.log('Connection to server kurento 2');
var createWebRtcEndpointFlag = false;
wsk2.on('message', function(_message) {
var message = JSON.parse(_message);
console.log('Connection received message Server2 :', _message);
switch(message.id) {
case 31:
relayUser2.mediaPipeline = message.result.value;
relayUser2.sessionId = message.result.sessionId;
console.log("relayUser2 createRtpEndpoint send:", createRtpEndpoint(wsk2, 39, relayUser2.sessionId, relayUser2.mediaPipeline));
break;
case 34:
/* 重点在34 39,将两个服务器的RtpEndpoint的sdp进行交换 */
console.log("relayUser1 invokeProcessAnswer send:", invokeProcessAnswer(wsk, 20, relayUser1.sessionId, relayUser1.WebRtcEndpointId, message.result.value));
break;
case 39:
relayUser2.events = new events.EventEmitter();
relayUser2.events.id = message.result.value;
relayUser2.WebRtcEndpointId = message.result.value;
console.log("relayUser2 invokeProcessOffer send:", invokeProcessOffer(wsk2, 34, relayUser2.sessionId, relayUser2.WebRtcEndpointId, relayUser1.sdpOffer));
break;
case 42:
viewerUser.events = new events.EventEmitter();
viewerUser.events.on('OnIceCandidate', function(event) {
console.log("viewerUser OnIceCandidate");
});
viewerUser.events.id = message.result.value;
viewerUser.WebRtcEndpointId = message.result.value;
if (viewerUser.candidatesQueue) {
while(viewerUser.candidatesQueue.length) {
var candidate = viewerUser.candidatesQueue.shift();
console.log("viewerUser invokeAddIceCandidate send:", invokeAddIceCandidate(wsk2, 47, viewerUser.sessionId, viewerUser.WebRtcEndpointId, candidate));
}
}
console.log("viewerUser subscribeCandidate send:", subscribeCandidate(wsk2, 45, viewerUser.sessionId, viewerUser.WebRtcEndpointId));
console.log("viewerUser invokeProcessOffer send:", invokeProcessOffer(wsk2, 44, viewerUser.sessionId, viewerUser.WebRtcEndpointId, viewerUser.sdpOffer));
console.log("viewerUser invokeGatherCandidates send:", invokeGatherCandidates(wsk2, 46, viewerUser.sessionId, viewerUser.WebRtcEndpointId));
break;
case 44:
var msg = JSON.stringify({
"id": "viewerResponse",
"response": "accepted",
"sdpAnswer": message.result.value
});
console.log("viewerUser ws send:", msg);
viewerUser.ws.send(msg);
break;
case 45:
console.log("relayUser2 invokeConnect send:", invokeConnect(wsk2, 33, relayUser2.sessionId, relayUser2.WebRtcEndpointId, viewerUser.WebRtcEndpointId));
break;
}
if (message.method == 'onEvent' && message.params.value.type == 'OnIceCandidate') {
if (true) {
if (viewerUser.WebRtcEndpointId == message.params.value.data.source) {
}
}
}
});
});
/*
客户端响应代码
*/
wss.on('connection', function(ws) {
nextUniqueId();
var connId = idCounter;
console.log('Connection received with :' + connId);
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 ' + connId + ' received message ' + _message);
switch (message.id) {
case 'presenter':
presenter(message.sdpOffer, ws);
break;
case 'viewer':
viewer(message.sdpOffer, ws);
break;
case 'onIceCandidate':
if (connId == presenterUser.connId) {
onIceCandidate(wsk, message.candidate, presenterUser, 17);
} else {
onIceCandidate(wsk2, message.candidate, viewerUser, 47);
}
break;
default:
ws.send(JSON.stringify({
id : 'error',
message : 'Invalid message ' + message
}));
break;
}
});
});
function presenter(_sdpOffer, _ws) {
if (presenterUser.connId != null) {
return;
}
presenterUser.sdpOffer = _sdpOffer;
presenterUser.ws = _ws;
presenterUser.connId = idCounter;
relayUser1.connId = idCounter;
relayUser2.connId = idCounter;
console.log("presenterUser createMediaPipeline send:", createMediaPipeline(wsk, 11));
}
function viewer(_sdpOffer, _ws) {
viewerUser.sdpOffer = _sdpOffer;
viewerUser.ws = _ws;
viewerUser.connId = idCounter;
viewerUser.sessionId = relayUser2.sessionId;
viewerUser.mediaPipeline = relayUser2.mediaPipeline
console.log("viewerUser createWebRtcEndpoint send:", createWebRtcEndpoint(wsk2, 42, viewerUser.sessionId, viewerUser.mediaPipeline));
}
function onIceCandidate(_ws, _candidate, _user, _id) {
if (!_ws || _user.WebRtcEndpointId == null) {
if (_user.candidatesQueue == null) {
_user.candidatesQueue = [];
}
_user.candidatesQueue.push(_candidate);
} else {
console.log("_user invokeAddIceCandidate send:", invokeAddIceCandidate(_ws, _id, _user.sessionId, _user.WebRtcEndpointId, _candidate));
}
}
app.use(express.static(path.join(__dirname, 'static')));
基于one2many的kms集群
1.媒体服务器1
1.1 创建媒体管道
1.2 创建输入WebRtcEndpoint和输出RtpEndpoint
1.3 WebRtcEndpoint设置客户端的Candidate
1.4 WebRtcEndpoint订阅Candidate、验证客户端的sdpOffer、获取Candidate
1.5 将验证返回的sdpAnswer发送给客户端
1.6 将从服务器获取的Candidate发送给客户端
1.7 连接输入WebRtcEndpoint和输出RtpEndpoint
1.8 获取RtpEndpoint的sdpOffer
媒体服务器2
2.1 创建媒体管道
2.2 创建输入RtpEndpoint
2.3 验证媒体服务器1的RtpEndpoint的sdpOffer
2.4 将验证返回的sdpAnswer发送给媒体服务器1的RtpEndpoint
观察者(媒体服务器2)
3.1 创建输出WebRtcEndpoint
3.2 WebRtcEndpoint设置客户端的Candidate
3.3 WebRtcEndpoint订阅Candidate、验证客户端的sdpOffer、获取Candidate
3.4 将验证返回的sdpAnswer发送给客户端
3.5 连接输入RtpEndpoint和输出WebRtcEndpoint