Kurento Media Server集群(KMS to KMS)

教程版本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

你可能感兴趣的:(Kurento Media Server集群(KMS to KMS))