摘要:示例當(dāng)用戶從連接或斷開鏈接時(shí),將被調(diào)用,該方法的回調(diào)函數(shù)包含兩個(gè)參數(shù)連接到相同房間名的用戶在我們的示例程序當(dāng)中,該方法的回調(diào)函數(shù)將建立一系列用于撥打給當(dāng)前已連接到房間內(nèi)的其他用戶的按鈕。
EasyRTC 概覽
EasyRTC基于webRTC。WebRTC是W3C/IETF用于瀏覽器間實(shí)時(shí)音視頻溝通以及數(shù)據(jù)傳輸?shù)囊粋€(gè)實(shí)現(xiàn)方案。WebRTC只需要一個(gè)輕量負(fù)荷的服務(wù)器就可以支持點(diǎn)對(duì)點(diǎn)(P2P)間的任何數(shù)據(jù)傳輸。
EasyRTC由客戶端(瀏覽器端)的JS庫與基于node.js的后端服務(wù)器組成。WebRTC已經(jīng)被各個(gè)瀏覽器(google chrome, firefox, opera, etc)所支持,因此無需額外的瀏覽器插件。
Google Chrome對(duì)WebRTC的API有著最廣泛的支持,Opera現(xiàn)在采用與Chrome相同的內(nèi)核引擎,因此所有API行為與chrome基本一致。Firefox對(duì)WebRTC的Data Channel有著十分良好的實(shí)現(xiàn),但僅提供了基礎(chǔ)的視頻功能。
一旦WebRTC被標(biāo)準(zhǔn)化,它將有著巨大的潛能為音視頻會(huì)議,多用戶游戲,以及許多其他的基于音視頻,數(shù)據(jù)傳輸?shù)膽?yīng)用提供支持。
如同其他的軟件,強(qiáng)大的功能往往伴隨著復(fù)雜的內(nèi)部實(shí)現(xiàn)。WebRTC有著十分曲折的學(xué)習(xí)曲線,對(duì)開發(fā)人員不夠友好。為了簡(jiǎn)化具體的開發(fā)流程,我們(Priologic)構(gòu)建了EasyRTC框架。
構(gòu)建一個(gè)基于WebRTC的應(yīng)用通常有如下步驟。
將本地?cái)z像頭、麥克風(fēng)獲取到的數(shù)據(jù)輸出成media stream對(duì)象
與信令服務(wù)器建立連接
通過瀏覽器與目標(biāo)用戶建立p2p通信
將media stream綁定到
通過使用EasyRTC,一些步驟可以被簡(jiǎn)化到一個(gè)簡(jiǎn)單的通信(call)當(dāng)中,
能極大地簡(jiǎn)化開發(fā)流程,尤其是當(dāng)開發(fā)人員需要投入更多的精力于多平臺(tái)支持當(dāng)中。
此文檔是編寫基于WebRTC應(yīng)用的一個(gè)基本教程,但并未包含EasyRTC的所有API。
術(shù)語callbakck(回調(diào)函數(shù))
Media Stream 瀏覽器音視頻輸出對(duì)象
Peer Connection 點(diǎn)對(duì)點(diǎn)連接
Server
安裝EasyRTC與獲取支持EasyRTC的安裝十分簡(jiǎn)單,多數(shù)平臺(tái)可在10分鐘內(nèi)完成。我們提供了Windows,Linux,Mac的安裝向?qū)АasyRTC的源碼可在[https://github.com/priologic/...]獲取。在doc目錄下可以獲取到客戶端與服務(wù)端的所有HTML說明文檔
視頻會(huì)議html 略
頁面載入完成(onload)后調(diào)用初始化函數(shù)(initialization function)。
初始化函數(shù)的最主要作用是調(diào)用EasyRTC.easyApp方法。該方法有如下參數(shù)
applicationName - String 應(yīng)用名,如"Company_Chat_Line"
self-video-id - String video標(biāo)簽id
array-of-caller-video-ids - Array 包含了其他用戶(除當(dāng)前用戶)的video標(biāo)簽id
successCallback - 連接成功時(shí)回調(diào)函數(shù)
初始化函數(shù)可以使用EasyRTC.setRoomOccupantListener來注冊(cè)一個(gè)回調(diào)函數(shù),以用于獲取當(dāng)前已連接到同一房間內(nèi)的其他用戶id,。
示例:
function my_init() { easyrtc.setRoomOccupantListener(loggedInListener) easyrtc.easyApp("Company_chat_line", "self", ["caller"], id => { console.info("My id is " + id) }) }
當(dāng)用戶從"Company_chat_line"連接或斷開鏈接時(shí),easyrtc.setRoomOccupantListener將被調(diào)用,該方法的回調(diào)函數(shù)包含兩個(gè)參數(shù):
String room name
Array 連接到相同房間名的用戶id
在我們的示例程序當(dāng)中,該方法的回調(diào)函數(shù)將建立一系列用于“撥打”給當(dāng)前已連接到房間內(nèi)的其他用戶的按鈕。
html 略
多數(shù)情況下可以忽略room_name參數(shù),除非你的應(yīng)用允許用戶同時(shí)連接到多個(gè)房間。
在現(xiàn)實(shí)的應(yīng)用當(dāng)中,我們不會(huì)使用easyrtc的默認(rèn)id當(dāng)作按鈕的label屬性。我們將使用類似姓名,職位等建立起與easyrtc id相關(guān)聯(lián)的內(nèi)容以用作按鈕的label屬性。
初始化一個(gè)call,我們只需要調(diào)用 easyrtc.call 方法,傳入目標(biāo)用戶的id,該方法包含三個(gè)回調(diào)函數(shù):
successCallback(id)
errorCallback(errorCode, errorText)
accepted(wasAccepted, id) 指明該call是否被接受
示例代碼:
function performCall(id) { easyrtc.call(id, id => { console.info("completed call to " + id) }, errorMessage => { console.error("err: " + errorMessage) }, (accepted, bywho) => { console.info(accepted ? "accepted" : "rejected" + " by " + bywho) }) }
html 略
視頻會(huì)議(Advanced)在上一節(jié),我們大致地描述了構(gòu)建一個(gè)視頻會(huì)議應(yīng)用的最簡(jiǎn)單情形。
在這一節(jié),我們將進(jìn)一步深入。
除了調(diào)用easyrtc.easyApp,你也可以調(diào)用easyrtc.initMediaSource來直接獲取本地設(shè)備的media stream,成功之后可以調(diào)用easyrtc.connect方法來連接到信令服務(wù)器。這也是easyrtc.easyApp的內(nèi)部實(shí)現(xiàn)。
html略
注意: easyrtc.getLocalStream和easyrtc.setVideoObjectSrc,前者用于當(dāng)easyrtc.initMediaSource調(diào)用完成,從本地?cái)z像頭和麥克風(fēng)獲取media stream,后者用于將media stream與video標(biāo)簽綁定。一起使用便可以十分便攜地供用戶實(shí)時(shí)觀察到他們自己的圖像。
我們還需要兩個(gè)其他函數(shù)
一個(gè)用于提供遠(yuǎn)程用戶的media stream
easyrtc.setStreamAcceptor((callerId, stream) => { let video = document.getElementById("caller") easyrtc.setVideoObjectSrc(video, stream) })
一個(gè)用于檢測(cè)遠(yuǎn)程用戶是否掛起(離線)。該函數(shù)用于清除對(duì)應(yīng)的video標(biāo)簽
easyrtc.setOnStreamClosed(callerId => { easyrtc.setVideoObjectSrc(document.getElementById("caller"), "") })
整個(gè)js文件如下
// 設(shè)置遠(yuǎn)程用戶的media stream 的關(guān)聯(lián)video標(biāo)簽 easyrtc.setStreamAcceptor((callerId, stream) => { let video = document.getElementById("caller") // 綁定media stream到video標(biāo)簽對(duì)象 easyrtc.setVideoObjectSrc(video, stream) }) // 當(dāng)遠(yuǎn)程media strem 關(guān)閉 easyrtc.setOnStreamClosed(callerId => { // 清除關(guān)聯(lián)的video標(biāo)簽內(nèi)容 easyrtc.setVideoObjectSrc(document.getElementById("caller"), "") }) // 初始化函數(shù) function my_init() { // 設(shè)置監(jiān)聽器,獲取當(dāng)前在線的用戶 easyrtc.setRoomOccupantListener(loggedInListener) // 連接成功處理函數(shù) let connectSuccess = myId => { console.info("My easyrtc id is " + myId) } // 連接失敗 創(chuàng)建本地media stream對(duì)象失敗時(shí)調(diào)用函數(shù) let connectFailure = (errorCode, errText) => { console.error(errText) } // 初始化本地media stream easyrtc.initMediaSource(() => { let selfVideo = document.getElementById("self") // 綁定本地media stream 到video tag easyrtc.setVideoObjectSrc(selfVideo, easyrtc.getLocalStream()) // 連接到服務(wù)器 easyrtc.connect("Company_Chat_line", connectSuccess, connectFailure) }, connectFailure) } // 當(dāng)獲取到當(dāng)前房間內(nèi)在線用戶 function loggedInListener(roomName, otherPeers) { let otherClientDiv = document.getElementById("otherClients") while(otherClientDiv.hasChildNodes()) { // 移除最后一個(gè) “text ” otherClientDiv.removeChild(otherClientDiv.lastChild) } for (let i in otherPeers) { let button = document.createElement("button") // 為每一個(gè)遠(yuǎn)程用戶創(chuàng)建一個(gè)按鈕監(jiān)聽器 button.onclick = easyrtcId => { // 發(fā)起連接 return (easyrtcId) => performCall(easyrtcId) }(i) let label = document.createTextNode(i) button.appendChild(label) otherClientDiv.appendChild(button) } } function performCall(id) { easyrtc.call(id, id => { console.info("completed call to " + id) }, (errorCode, errText) => { console.error("err: " + errorText) }, (accepted, bywho) => { console.info(accepted ? "accepted" : "rejected" + " by " + bywho) }) }使用多個(gè)本地media stream源
使用多個(gè)media stream的基本思想是,你需要為每個(gè)media stream命名。
當(dāng)你調(diào)用initMediaSource,它的第三個(gè)參數(shù)便是media stream的名字,
如:
easyrtc.initMediaStream(success, failure, yourname)
如果你沒有傳入第三個(gè)參數(shù),則該media stream會(huì)得到一個(gè)默認(rèn)的"default"。
使用easyrtc.getLocalMediaIds以獲取所有本地media stream的名字
let ids = easyrtc.getLocalMediaIds() ids.map(id => console.info(id))
當(dāng)你初始化一個(gè)call,可以傳入一個(gè)stream name的數(shù)組作為第五個(gè)參數(shù),同樣的,當(dāng)你接受call時(shí),你可以傳入一個(gè)stream name的數(shù)組作為accept回調(diào)函數(shù)的第二個(gè)參數(shù)。
easyrtc.call(otherEasyrtcId, successCB, failCB, wasAcceptedCB, ["first_name", "second_name", "etc"]) easyrtc.setAcceptChecker((otherGuy, acceptCallback) => { acceptCallback(true, ["first_name", "second_name"]) })
你也可以通過使用easyrtc.addStreamToCall向一個(gè)已存在的call添加meida stream。該方法接受三個(gè)參數(shù),接受stream的id,stream的名字,以及一個(gè)optional的回調(diào)處理函數(shù)。
注意:EasyApp 框架并不是專門為多media stream而設(shè)計(jì)的。它的初衷便是假定只有單個(gè)本地media stream。
如果你想使用media stream,那么你就必須自己將這些media stream綁定到video標(biāo)簽。
EasyRTC允許注冊(cè)一個(gè)在用戶每次收到call時(shí)都將被調(diào)用的函數(shù)。該函數(shù)接受遠(yuǎn)程用戶的id,以及一個(gè)報(bào)告函數(shù)(reporting function)作為參數(shù),報(bào)告函數(shù)接受一個(gè)參數(shù),true接受對(duì)話,false拒絕對(duì)話。
easyrtc.setAcceptChecker( function(easyrtcid, acceptor){ if( easyrtc.idToName(easyrtcid) === "Fred" ){ acceptor(true); } else if( easyrtc.idToName(easyrtcid) === "Barney" ){ setTimeout( function(){ acceptor(true, ["myOtherCam"]); // myOtherCam presumed to a streamName }, 10000); } else{ acceptor(false); } });加入或離開房間
Room是EasyRTC的一個(gè)隔離(compartmentalize)功能,目的是為用戶建立起一個(gè)個(gè)“chat rooms”。
房間的行為受服務(wù)器安裝的EasyRTC Server的配置所影響(詳見服務(wù)器模塊文檔)。默認(rèn)行為如下:
除非用戶在連接前指定了所要加入的房間名,否則將會(huì)加入默認(rèn)的“default”房間。
一個(gè)用戶可以是多個(gè)房間的成員
每個(gè)用戶所加入的任一個(gè)房間發(fā)生變化時(shí)(用戶的加入,離開),都會(huì)觸發(fā)roomOccupantListener函數(shù)
加入一個(gè)不存在的房間將會(huì)創(chuàng)建它
加入房間
easyrtc.joinRoom(roomName, roomParameters, successCallback, failureCallback)
其中,roomParameters是Application specific(不詳),可以為空。joinRoom可以在任何時(shí)候調(diào)用任意多次,但successCallback, failureCallback只會(huì)在成功與信令服務(wù)器建立連接之后才會(huì)被調(diào)用。
離開房間
easyrtc.leaveRoom(roomName, successCallback, failureCallback)
leaveRoom的性質(zhì)同joinRoom
監(jiān)聽Error你可以通過easyrtc.setOnError注冊(cè)一個(gè)error callback用以處理錯(cuò)誤。該函數(shù)接受一個(gè)形如{"errorCode": "errorCode", "errorText": "errorText"}的對(duì)象。
easyrtc.setOnError(errEvent => { console.error(errEvent.errorText) })發(fā)送消息
你可以通過調(diào)用easyrtc.sendDataWS來使用websocket通信,
easyrtc.sendDataWS(destination, messageType, messageData, ackHandler) easyrtc.sendDataWS("xkxkxkxkxk9c93", "contactInfo", {firstName: "jack", lastName: "smith"}, ackMsg => { // ackMsg 為來自服務(wù)器的確認(rèn)信息 if (ackMsg.msgType === "error") { console.error(ackMsg.msgData.errorText) } })
注意: 通過websocket通信意味著你指定信息要通過服務(wù)器轉(zhuǎn)發(fā)
destination 可以是peer的id,或者是一個(gè)指定了一個(gè)或多個(gè)目標(biāo)id的js對(duì)象,或者房間(詳見文檔)。
messageType需要自行指定,ackHandler處理來自服務(wù)器的確認(rèn)信息
處理來自其他用戶的信息:
easyrtc.setPeerListener((sender_id, msgType, msgData, targeting) => {
if (msgType === "contactInfo") { console.info(sender_id + " is named " + msgData.firstName + " " + msgData.lastName) }
})
其中,當(dāng)使用WebRTC的data channel發(fā)送數(shù)據(jù)時(shí),targeting為null,否則為{targetEasyrtcid, targetGroup, targetRoom}當(dāng)中的一個(gè)。
你也可以為特定的msgType或sender指定監(jiān)聽器,在這種情況下,每次將只有一個(gè)監(jiān)聽器會(huì)被調(diào)用,指定的監(jiān)聽器將被優(yōu)先調(diào)用。
使用Data Channels在使用data channel之前,發(fā)送者和接收者都必須啟用data channels。
easyrtc.enableDataChannels(true)
之后便可監(jiān)聽與特定用戶的datachannel的 ready 和 close 事件,
easyrtc.setDataChannelOpenListener(sourceEasyrtcId => console.info("channel is open ") ) easyrtc.setDataChannelCloseListener(sourceEasyrtcid => console.info("channel is close ") )
open監(jiān)聽器被調(diào)用之后,便可以通過easyrtc.sendDataP2P來發(fā)送消息:
easyrtc.sendDataP2P(targetEasyrtcid, "contactInfo", {firstName: "jack", lastName: "smith"})
監(jiān)聽data channels消息與websocket消息一致
獲取當(dāng)前連接數(shù)通過easyrtc.getConnectionCount來獲取當(dāng)前用戶的連接數(shù),該函數(shù)返回一個(gè)number
掛起通過easyrtc.hangup(peerId)來掛起與特定用戶的連接
easyrtc.hangupAll()來掛起與所有用戶的連接
與服務(wù)器斷開鏈接easyrtc.disconnect()
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/92451.html
摘要:畢竟官方文檔才是未經(jīng)提煉的純技術(shù)點(diǎn),讀書不能只讀二手書。目前網(wǎng)上能找到的中文文檔基本都是基于的,但截至此文發(fā)布,最新的穩(wěn)定版都已經(jīng)是了。翻譯過程中主要參考官方英文文檔,以及極客學(xué)院的官方文檔中文翻譯。 前言 相信很多開發(fā)者和我一樣,在學(xué)習(xí)一門技術(shù)的時(shí)候,通過網(wǎng)上的各種教程和視頻入門之后會(huì)發(fā)現(xiàn)自己遇到一個(gè)上升瓶頸。造成這個(gè)瓶頸的很大一部分原因,我認(rèn)為是進(jìn)階教程的知識(shí)點(diǎn)過于分散,同時(shí)高質(zhì)量...
摘要:關(guān)于本文檔本文檔的目的,是全面地解釋的,即可作為參考文檔,同時(shí)也包含了概念的講解。但有的全新的實(shí)驗(yàn)性的,或者存在危險(xiǎn)性的部分則會(huì)被重新設(shè)計(jì)。穩(wěn)定級(jí)別鎖定只會(huì)有安全性能或相關(guān)的修復(fù)。不接受對(duì)此做修改的建議。 關(guān)于本文檔 本文檔的目的,是全面地解釋Node.js的API,即可作為參考文檔,同時(shí)也包含了概念的講解。每個(gè)章節(jié)都描述了一個(gè)內(nèi)置模塊或一個(gè)高階概念(high-level concep...
閱讀 2436·2019-08-30 15:52
閱讀 2237·2019-08-30 12:51
閱讀 2832·2019-08-29 18:41
閱讀 2812·2019-08-29 17:04
閱讀 810·2019-08-29 15:11
閱讀 1719·2019-08-28 18:02
閱讀 3602·2019-08-26 10:22
閱讀 2510·2019-08-26 10:12