XMPP即時通信協議使用(十二)——基於xmpp搭建簡單的局域網WebRTC

建立HTML和JS

ofwebrtc.htmljavascript

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>HTML5 GetUserMedia Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
	<script src='jquery-1.9.1.min.js'></script>
    <script src='strophe.min.js'></script>
    <script src='ofwebrtc.js'></script>
</head>
<body>

    <p id="info"></p>
    <p>----------------------------------------------------------------</p>

    JID:<input type="text" id="input-jid" value="">
    <br>
    密碼:<input type="password" id="input-pwd" value="">
    <br>
    <button id="btn-login">登陸</button><br>

    <br>
    目標JID:
    <input type="text" id="input-contacts" value="mytest1@pc-20170308pkrs">
    <button id="btn-call">Call</button>
    <p>----------------------------------------------------------------</p>

    Local: <br>
    <video id="localVideo" autoplay></video><br>
    Remote: <br>
    <video id="remoteVideo" autoplay></video>
</body>
</html>

ofwebrtc.jshtml

// XMPP幫助類
// boshService: XMPP服務器BOSH地址
function XMPPHelper(boshService) {
    var _this = this;

    // XMPP服務器BOSH地址
    _this.boshService = boshService;

    // XMPP鏈接
    _this.connection = null;

    // 當前狀態是否鏈接
    _this.connected = false;

    // 當前登陸的JID
    _this.jid = "";

    // 收到消息後的業務回調方法
    _this.messageCallback = null;
    _this.setMessageCallback = function (messageCallback) {
        _this.messageCallback = messageCallback;
    }

    // 接收到<message>
    var onMessage = function (msg) {
        console.log('--- msg ---', msg);

        // 解析出<message>的from、type屬性,以及body子元素
        var from = msg.getAttribute('from');
        var type = msg.getAttribute('type');
        var elems = msg.getElementsByTagName('body');
        var json = JSON.parse(HtmlUtil.htmlDecodeByRegExp(elems[0].innerHTML));
		console.log('--- json ---', json);
        json.fromjid = from;

        if (type === 'chat') {
            _this.messageCallback(json);
        }

        return true;
    }

    // 鏈接狀態改變的事件
    var onConnect = function (status) {
        console.log('status: ' + status)
        if (status == Strophe.Status.CONNFAIL) {
            $('#info').html("鏈接失敗!");
        } else if (status == Strophe.Status.AUTHFAIL) {
            $('#info').html("登陸失敗!");
        } else if (status == Strophe.Status.DISCONNECTED) {
            $('#info').html("鏈接斷開!");
            _this.connected = false;
        } else if (status == Strophe.Status.CONNECTED) {
            $('#info').html("鏈接成功!");
            _this.connected = true;

            // 當接收到<message>節,調用onMessage回調函數
            _this.connection.addHandler(onMessage, null, 'message', null, null, null);

            // 首先要發送一個<presence>給服務器(initial presence)
            _this.connection.send($pres().tree());
        }
    };

    // 登陸
    _this.login = function (jid, password) {
        _this.connection = new Strophe.Connection(_this.boshService);
        _this.connection.connect(jid, password, onConnect);
        _this.jid = jid;
    };

    _this.sendMessage = function (tojid, type, data) {
        if (_this.connected === false) {
            alert("請先登陸!!!");
            return;
        }

        var msg = $msg({
            to: tojid,
            from: _this.jid,
            type: 'chat'
        }).c("body", null, JSON.stringify({
            type: type,
            data: data
        }));
        _this.connection.send(msg.tree());
    };
}

// WebRTC幫助類
// xmppHelper:XMPP幫助實例
// localVideo:本地視頻顯示的DOM
// remoteVideo:遠端視頻顯示的DOM
function WebRTCHelper(xmppHelper, localVideo, remoteVideo) {
    var _this = this;

    // 對方用戶
    _this.tojid = null;

    // 建立PeerConnection實例 (參數爲null則沒有iceserver,即便沒有stunserver和turnserver,仍可在局域網下通信)
    _this.pc = new webkitRTCPeerConnection(null);

    _this.hasBindLocalVideo = false;

    // 發送ICE候選到其餘客戶端
    _this.pc.onicecandidate = function(event){
        if (event.candidate !== null && _this.tojid !== null) {
            console.log('----------- onicecandidate ------------');
            console.log('candidate', event.candidate);

			xmppHelper.sendMessage(_this.tojid, 'candidate', event.candidate);
        }
    };

    // 若是檢測到媒體流鏈接到本地,將其綁定到一個video標籤上輸出
    _this.pc.onaddstream = function(event){
		console.log('----------- onaddstream ------------');
        remoteVideo.src = URL.createObjectURL(event.stream);
    };

    // 發送offer和answer的函數,發送本地session描述
    var sendOfferFn = function(desc){
        console.log('----------- sendOfferFn ------------');
        console.log('desc', desc);
        _this.pc.setLocalDescription(desc);
        
        xmppHelper.sendMessage(_this.tojid, 'offer', desc);
    };
    var sendAnswerFn = function(desc){
        console.log('----------- sendAnswerFn ------------');
        console.log('desc', desc);
        _this.pc.setLocalDescription(desc);

        xmppHelper.sendMessage(_this.tojid, 'answer', desc);
    };
	var mediaConfig =  { video: true };
    // 綁定本地視頻流
    var bindLocalVideo = function (callback) {
        if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
			navigator.mediaDevices.getUserMedia(mediaConfig).then(function(stream) {
				//綁定本地媒體流到video標籤用於輸出
				localVideo.src = window.URL.createObjectURL(stream);
				//向PeerConnection中加入須要發送的流
				_this.pc.addStream(stream);
				callback();
			});
		}
		
    };

	
    // 開始視頻通信
    _this.start = function (tojid) {
        _this.tojid = tojid;

        if (_this.hasBindLocalVideo === false) {
            bindLocalVideo(function () {
                // 發送一個offer信令
                _this.pc.createOffer(sendOfferFn, function (error) {
                    console.log('Failure callback: ' + error);
                });
            });
            _this.hasBindLocalVideo = true;
        } else {
            // 發送一個offer信令
            _this.pc.createOffer(sendOfferFn, function (error) {
                console.log('Failure callback: ' + error);
            });
        }
    };

    // 收到對方信息後的處理
    _this.onMessage = function (json) {
        console.log('onMessage: ', json);
		console.log('json.data: ', json.data);

        if (_this.tojid === null) {
            _this.tojid = json.fromjid;
        }

        if (json.type === 'candidate') {
            _this.pc.addIceCandidate(new RTCIceCandidate(json.data));
        } else {
            _this.pc.setRemoteDescription(new RTCSessionDescription(json.data));
            if (json.type === 'offer') {
                if (_this.hasBindLocalVideo === false) {
                    bindLocalVideo(function () {
                        _this.pc.createAnswer(sendAnswerFn, function (error) {
                            console.log('Failure callback: ' + error);
                        });
                    });
                    _this.hasBindLocalVideo = true;
                } else {
                    _this.pc.createAnswer(sendAnswerFn, function (error) {
                        console.log('Failure callback: ' + error);
                    });
                }
            }
        }
    }
}

$(document).ready(function() {
    // 實例化XMPP和WebRTC幫助類
    var xmppHelper = new XMPPHelper('http://localhost:7070/http-bind/');
    var webRTCHelper = new WebRTCHelper(xmppHelper, document.getElementById('localVideo'), document.getElementById('remoteVideo'));

    // XMPP收到消息後轉給WebRTC
    xmppHelper.setMessageCallback(webRTCHelper.onMessage);

    $('#btn-login').click(function() {
        console.log('jid: ' + $("#input-jid").val());
        console.log('pwd: ' + $("#input-pwd").val());
        xmppHelper.login($("#input-jid").val(), $("#input-pwd").val());
    });

    $('#btn-call').click(function() {
        if($("#input-contacts").val() == '') {
            alert("請輸入目標用戶!");
            return;
        }
        tojid = $("#input-contacts").val();

        webRTCHelper.start(tojid);
    });
});

var HtmlUtil = {
        /*1.用瀏覽器內部轉換器實現html轉碼*/
        htmlEncode:function (html){
            //1.首先動態建立一個容器標籤元素,如DIV
            var temp = document.createElement ("div");
            //2.而後將要轉換的字符串設置爲這個元素的innerText(ie支持)或者textContent(火狐,google支持)
            (temp.textContent != undefined ) ? (temp.textContent = html) : (temp.innerText = html);
            //3.最後返回這個元素的innerHTML,即獲得通過HTML編碼轉換的字符串了
            var output = temp.innerHTML;
            temp = null;
            return output;
        },
        /*2.用瀏覽器內部轉換器實現html解碼*/
        htmlDecode:function (text){
            //1.首先動態建立一個容器標籤元素,如DIV
            var temp = document.createElement("div");
            //2.而後將要轉換的字符串設置爲這個元素的innerHTML(ie,火狐,google都支持)
            temp.innerHTML = text;
            //3.最後返回這個元素的innerText(ie支持)或者textContent(火狐,google支持),即獲得通過HTML解碼的字符串了。
            var output = temp.innerText || temp.textContent;
            temp = null;
            return output;
        },
        /*3.用正則表達式實現html轉碼*/
        htmlEncodeByRegExp:function (str){  
             var s = "";
             if(str.length == 0) return "";
             s = str.replace(/&/g,"&amp;");
             s = s.replace(/</g,"&lt;");
             s = s.replace(/>/g,"&gt;");
             s = s.replace(/ /g,"&nbsp;");
             s = s.replace(/\'/g,"&#39;");
             s = s.replace(/\"/g,"&quot;");
             return s;  
       },
       /*4.用正則表達式實現html解碼*/
       htmlDecodeByRegExp:function (str){  
             var s = "";
             if(str.length == 0) return "";
             s = str.replace(/&amp;/g,"&");
             s = s.replace(/&lt;/g,"<");
             s = s.replace(/&gt;/g,">");
             s = s.replace(/&nbsp;/g," ");
             s = s.replace(/&#39;/g,"\'");
             s = s.replace(/&quot;/g,"\"");
             return s;  
       }
    };

測試

一、打開Chrome,訪問ofwebrtc.html頁面,輸入JID(如admin@pc-20170308pkrs)、密碼,點擊登陸,提示鏈接成功。java

二、再打開一個Chrome訪問ofwebrtc.html頁面,輸入JID(如mytest1@pc-20170308pkrs),密碼,點擊登陸,提示鏈接成功。jquery

以後在目標JID輸入上個頁面的JID(admin@pc-20170308pkrs)點擊Call,會提示使用攝像頭,選擇容許。web

三、上個頁面也會彈出使用攝像頭提示,點擊容許正則表達式

四、效果json


後續有時間繼續補充優化... 瀏覽器

相關文章
相關標籤/搜索