與Kurento Media Server交互,One-To-Many

教程版本V6.10.0

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 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"
}
返回(可以判斷是否成功)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乏苦,更是在濱河造成了極大的恐慌,老刑警劉巖尤筐,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汇荐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盆繁,警方通過查閱死者的電腦和手機(jī)掀淘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來油昂,“玉大人革娄,你說我怎么就攤上這事∶岬” “怎么了拦惋?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)安寺。 經(jīng)常有香客問我厕妖,道長(zhǎng),這世上最難降的妖魔是什么挑庶? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任言秸,我火速辦了婚禮,結(jié)果婚禮上挠羔,老公的妹妹穿的比我還像新娘井仰。我一直安慰自己,他們只是感情好破加,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布俱恶。 她就那樣靜靜地躺著,像睡著了一般范舀。 火紅的嫁衣襯著肌膚如雪合是。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天锭环,我揣著相機(jī)與錄音聪全,去河邊找鬼。 笑死辅辩,一個(gè)胖子當(dāng)著我的面吹牛难礼,可吹牛的內(nèi)容都是我干的娃圆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛾茉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼讼呢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谦炬,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤悦屏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后键思,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體础爬,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年吼鳞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了看蚜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赔桌,死狀恐怖失乾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纬乍,我是刑警寧澤碱茁,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站仿贬,受9級(jí)特大地震影響纽竣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茧泪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一蜓氨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧队伟,春花似錦穴吹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锈颗,卻和暖如春顷霹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背击吱。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工淋淀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人覆醇。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓朵纷,卻偏偏與公主長(zhǎng)得像炭臭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袍辞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,097評(píng)論 1 32
  • The Inner Game of Tennis W Timothy Gallwey Jonathan Cape ...
    網(wǎng)事_79a3閱讀 12,046評(píng)論 3 20
  • 我去陳紫玲家樓下的時(shí)候徽缚,她正好出來,她很自然的挽住我胳膊問革屠,晚飯吃什么。那一天我們走了很多地方排宰,我走的很仔細(xì)似芝。后來...
    風(fēng)陪伴的夏天閱讀 696評(píng)論 0 2
  • 我不知道我怎么了,像是患了抑郁癥板甘,很痛苦党瓮,很難受,很壓抑盐类,不想上班寞奸,感覺上班就是一種束縛,也找不到自我的價(jià)值在跳。不知...
    蘇貴妃閱讀 197評(píng)論 0 0
  • 善人在成長(zhǎng)的每一段經(jīng)歷中枪萄,都留給同齡人無法去模仿,去讀懂的內(nèi)涵猫妙。善人在沒有出去扛活時(shí)瓷翻,在自己家里,也是不同凡人割坠。他...
    王澤華wzh閱讀 522評(píng)論 0 0