Webrtc穿透转发通讯1-网页

来源:互联网 发布:unity3d制作复杂模型 编辑:程序博客网 时间:2024/06/11 19:07

Webrtc穿透转发通讯1-网页


  • Webrtc穿透转发通讯2-windows

Webrtc应用模式

本文记录一个最基本webrtc网页类服务端对多个客户端模式通讯。基本作用就是实现以一个浏览器网页作为服务端,其它作为客户端交互由服务端网页提供的视频以及数据,由于浏览器种类众多,这里只以chrome和360最新版本浏览器为例,在这里将演示浏览器的使用方法。

组成部分:

组成部分

需要注意的地方:
信令服务器
这里以websocket作为信令通信方式,浏览器服务端需要连接保持,以保证客户浏览器随时可以加入。
客户端通过信令服务器接收从服务端返回的应答后退出信令服务。

简易流程图
流程图(客户端)           流程图(服务端)

这里写图片描述

代码:
服务端代码
index_server.html

<!DOCTYPE html><html><head>    <meta charset="utf-8">    <title>webrtc 服务端</title></head><body>    <div class="select">        <label for="audioSource">Audio input source: </label><select id="audioSource"></select>    </div>    <div class="select">        <label for="audioOutput">Audio output destination: </label><select id="audioOutput"></select>    </div>    <div class="select">        <label for="videoSource">Video source: </label><select id="videoSource"></select>    </div>    <input id="Button1" type="button" value="连接" onclick="connect()" />    <div id="labe2"></div>    <br /><br />    <video id="vidLocal" autoplay controls muted style="display: none"></video>    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>    <script src="rtc_server.js"></script>    <script>        var t = null;        function connect()        {            if (document.getElementById('Button1').value == "连接")            {                wsconnect();                //测试                if(t == null)                {                    var sendNum = 0;                    t = setInterval(function () {                        var connNum = 0;                        for (var socketId in dataChannels) {                            connNum++;                        }                        for (var socketId in dataChannels) {                            var sdata = {};                            sdata["content"] = "Hello RTC Server," + sendNum++ + ",server peerconn.num:" + connNum;                            rtc_send(sdata, socketId);                        }                    }, 1000);                }            }            else            {                delAllPeerConn();                document.getElementById('Button1').value = "连接";            }        }        connect();    </script></body></html>

rtc_server.js

/**  * 简单的基于webrtc的网页服务端  * Simple based on webrtc web service side *   * zhaiye * tgmaa@163.com *   * 本例子实现一个网页客户端与网页服务端交互的简单程序。  *  * This example a web client, a simple example of interacting with the server. *   *///信令服务var host = "tgmaa.cn";var UserID = "test";var Password = "a9f696454710384bf228047acba4d5ef";var nativePeerConnection = window.RTCPeerConnection;var nativeRTCSessionDescription = window.RTCSessionDescription;var nativeRTCIceCandidate = (window.RTCIceCandidate);var rtc_ws = null;var dataChannels = {};var ClientPeerConnList = {};var localMediaStream = null;var pcConstraints = {    'optional': []};//stun/turnvar servers = {    iceServers:             [                 { "url": "stun:stun.l.google.com:19302" },                 { "url": "stun:stun.xten.com" },             ]};//数据通道发送测试var sendTryCount = 0;function rtc_send(msg, socketId) {    if (dataChannels[socketId] && dataChannels[socketId].readyState == "open") {        dataChannels[socketId].send(JSON.stringify(msg));        console.log("Message sending success!");    }    else {        if (sendTryCount++ == 10)        {            sendTryCount = 0;            delSinglePeerConn(socketId);        }        console.log("Message sending failed!");    }}var testNum = 0;function setDataChannel(dc) {    dc.onerror = function (error) {        console.log("DataChannel Error:", error);    };    dc.onmessage = function (event) {        labe2.innerHTML = event.data;    };    dc.onopen = function () {        console.log("DataChannel open");    };    dc.onclose = function () {        console.log("DataChannel is Closed");        for (var socketId in dataChannels) {            if (dataChannels[socketId] == dc) {                delSinglePeerConn(socketId);            }        }    };}function delSinglePeerConn(socketId){    labe2.innerHTML = "DataChannel is Closed remove:" + socketId;    dataChannels[socketId].close();    delete dataChannels[socketId];    ClientPeerConnList[socketId].close();    delete ClientPeerConnList[socketId];}function delAllPeerConn() {    for (var socketId in dataChannels) {        delSinglePeerConn(socketId);    }    if (rtc_ws) {        rtc_ws.close();        rtc_ws = null;    }    labe2.innerHTML = "已关闭";    document.getElementById('Button1').value = "连接";}//创建peer连接function createPeerConnection(socketId, sdp) {    var pc = new nativePeerConnection(servers, pcConstraints);    ClientPeerConnList[socketId] = pc;    if (localMediaStream) {        pc.addStream(localMediaStream);    }    dataChannels[socketId] = pc.createDataChannel("sendDataChannel", null);    setDataChannel(dataChannels[socketId]);    pc.onaddstream = function (e) {        console.log("pc.onaddstream");    };    pc.onicecandidate = function (event) {        if (event.candidate != null)        {            console.log("pc.onicecandidate:" + JSON.stringify(event.candidate));            var sdata = {};            sdata["command"] = "__OnIceCandidate";            sdata["socketId"] = socketId;            sdata["sdp_mid"] = event.candidate.sdpMid;            sdata["sdp_mline_index"] = event.candidate.sdpMLineIndex;            sdata["sdp"] = event.candidate.candidate;            ws_Send(sdata);        }    };    pc.ondatachannel = function (e) {        console.log("pc.ondatachannel");    };    pc.createOffer = function (e) {        console.log("pc.createOffer");    };    pc.setRemoteDescription(new nativeRTCSessionDescription({ type: "offer", sdp: sdp }),         function () {             pc.createAnswer(function (session_desc) {                 pc.setLocalDescription(session_desc);                 var sdata = {};                 sdata["command"] = "__OnSuccessAnswer";                 sdata["socketId"] = socketId;                 sdata["sdp"] = session_desc.sdp;                 ws_Send(sdata);             }, function (errorInformation) {                 console.log(errorInformation);             });         },         function (errorInformation) {             console.log('setRemoteDescription error: ' + errorInformation);    });}//信令服务function ws_Send(data) {    try {        rtc_ws.send(JSON.stringify(data));    }    catch (ex) {        console.log("Message sending failed!");    }}function wsconnect() {    rtc_ws = new WebSocket("ws://" + host + ":9000");    rtc_ws.onopen = function () {        var sdata = {};        sdata["command"] = "__UserCreate";        var udata = {};        udata["UserID"] = UserID;        udata["Password"] = Password;        sdata["udata"] = udata;        ws_Send(sdata);        console.log("Socket connected!");        labe2.innerHTML = "Socket connected!";        ////与信令服务器保持连接,允许新加入客户端        //setInterval(function () {        //    ws_Send(0);        //}, 1000 * 20);        document.getElementById('Button1').value = "断开";    };    rtc_ws.onclose = function () {        console.log("webSocket connection has been disconnected!");        delAllPeerConn();    };    rtc_ws.onerror = function (err) {        labe2.innerHTML = "Socket error :" + err.message;    };    rtc_ws.onmessage = function (Message) {        var obj = JSON.parse(Message.data);        var command = obj.command;        switch (command) {            case "__OnLoginSuccessful": {                if (!localMediaStream)                    getLocalStream();            }            break;            case "__OnError": {                labe2.innerHTML = obj.info;            }            break;            case "__Offer": {                createPeerConnection(obj.socketId, obj.desc.sdp);            }            break;        }    };}//获取相关音视频设备navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);var videoElement = document.getElementById('vidLocal');var audioInputSelect = document.querySelector('select#audioSource');var audioOutputSelect = document.querySelector('select#audioOutput');var videoSelect = document.querySelector('select#videoSource');var selectors = [audioInputSelect, audioOutputSelect, videoSelect];audioInputSelect.onchange = getLocalStream;videoSelect.onchange = getLocalStream;function handleError(error) {    console.log('navigator.getUserMedia error: ', error);}function gotDevices(deviceInfos) {    // Handles being called several times to update labels. Preserve values.    var values = selectors.map(function (select) {        return select.value;    });    selectors.forEach(function (select) {        while (select.firstChild) {            select.removeChild(select.firstChild);        }    });    for (var i = 0; i !== deviceInfos.length; ++i) {        var deviceInfo = deviceInfos[i];        var option = document.createElement('option');        option.value = deviceInfo.deviceId;        if (deviceInfo.kind === 'audioinput') {            option.text = deviceInfo.label ||                'microphone ' + (audioInputSelect.length + 1);            audioInputSelect.appendChild(option);        } else if (deviceInfo.kind === 'audiooutput') {            option.text = deviceInfo.label || 'speaker ' +                (audioOutputSelect.length + 1);            audioOutputSelect.appendChild(option);        } else if (deviceInfo.kind === 'videoinput') {            option.text = deviceInfo.label || 'camera ' + (videoSelect.length + 1);            videoSelect.appendChild(option);        } else {            console.log('Some other kind of source/device: ', deviceInfo);        }    }    selectors.forEach(function (select, selectorIndex) {        if (Array.prototype.slice.call(select.childNodes).some(function (n) {          return n.value === values[selectorIndex];        })) {            select.value = values[selectorIndex];        }    });}function gotStream(stream) {    window.stream = stream; // make stream available to console    //videoElement.srcObject = stream;    //videoElement.onloadedmetadata = function (e) {    //    videoElement.play();    //};    localMediaStream = stream;    console.log("localMediaStream chang.");    // Refresh button list in case labels have become available    return navigator.mediaDevices.enumerateDevices();}function getLocalStream() {    if (window.stream) {        window.stream.getTracks().forEach(function (track) {            track.stop();        });    }    //获取选择的设备    var audioSource = audioInputSelect.value;    var videoSource = videoSelect.value;    //过滤设备    var constraints = {        audio: { deviceId: audioSource ? { exact: audioSource } : undefined },        video: {            deviceId: videoSource ? { exact: videoSource } : undefined, "width": {                "max": "640"            },            "height": {                "max": "480"            },            "frameRate": {                "max": "25"            }        }    };    navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).catch(handleError);}

客户端代码
index_client.html

<!DOCTYPE html><html><head>    <meta charset="utf-8">    <title>webrtc 客户端</title></head><body>    <input id="Button1" type="button" value="连接" onclick="connect()" />    <div id="labe2"></div>    <br /><br />    <video id="vid2" autoplay controls ></video>    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>    <script src="rtc_client.js"></script>    <script>        window.onbeforeunload = function () {            delPeerConn();        }        var t = null;        function connect()        {            if (document.getElementById('Button1').value == "连接") {                wsconnect();                if (t == null)                {                    //测试                    var testNum = 0;                    t = setInterval(function () {                        var sdata = {};                        sdata["socketId"] = cursocketId;                        sdata["content"] = "Hello RTC Client," + testNum++;                        if (document.getElementById('Button1').value == "断开")                            rtc_send(sdata);                    }, 1000);                }            }            else {                delPeerConn();            }        }        connect();    </script></body></html>

rtc_client.js

/**  * 简单的基于webrtc的网页客户端  * Simple based on webrtc web client *   * zhaiye * tgmaa@163.com *   * 本例子实现一个网页客户端与网页服务端交互的简单程序。  *  * This example a web client, a simple example of interacting with the server. *  *///信令服务var host = "tgmaa.cn";var UserID = "test";var Password = "a9f696454710384bf228047acba4d5ef";var nativePeerConnection = window.RTCPeerConnection;var nativeRTCSessionDescription = window.RTCSessionDescription;var nativeRTCIceCandidate = (window.RTCIceCandidate);var rtc_ws = null;var ServerPeerConn = null;var dataChannel = null;var cursocketId;var pcConstraints = {    'optional': []};//stun/turnvar servers = {    iceServers:             [                 { "url": "stun:stun.l.google.com:19302" },                 { "url": "stun:stun.xten.com" },             ]};function rtc_send(msg) {    console.log("rtc_send");    if (dataChannel && dataChannel.readyState == "open") {        dataChannel.send(JSON.stringify(msg));    }    else {        delPeerConn();    }}function setDataChannel(dc) {    dc.onerror = function (error) {        console.log("DataChannel Error:", error);    };    dc.onmessage = function (event) {        labe2.innerHTML = event.data;    };    dc.onopen = function () {        console.log("DataChannel is onopen");        document.getElementById('Button1').value = "断开";    };    dc.onclose = function () {        console.log("DataChannel is Closed");        delPeerConn();        document.getElementById('Button1').value = "连接";        labe2.innerHTML = "已关闭";    };}function delPeerConn() {    if (dataChannel) {        dataChannel.close();        ServerPeerConn.close();        dataChannel = ServerPeerConn = null;    }    if (rtc_ws) {        rtc_ws.close();        rtc_ws = null;    }}function createPeerConnection() {    console.log("createPeerConnection...");    ServerPeerConn = new nativePeerConnection(servers, pcConstraints);    // optional data channel    dataChannel = ServerPeerConn.createDataChannel("sendDataChannel", null);    setDataChannel(dataChannel);    ServerPeerConn.onaddstream = function (e) {        try {            console.log("remote media connection success!");            var vid2 = document.getElementById('vid2');            vid2.srcObject = e.stream;            vid2.onloadedmetadata = function (e) {                vid2.play();            };        } catch (ex) {            console.log("Failed to connect to remote media!", ex);        }    };    ServerPeerConn.onicecandidate = function (event) {        console.log("ServerPeerConn!");    };    ServerPeerConn.ondatachannel = function (event) {        dataChannel = event.channel;        setDataChannel(dataChannel);    }    var offerOptions = {        // New spec states offerToReceiveAudio/Video are of type long (due to        // having to tell how many "m" lines to generate).        // http://w3c.github.io/webrtc-pc/#idl-def-RTCOfferAnswerOptions.        offerToReceiveAudio: 1,        offerToReceiveVideo: 1,        iceRestart: 0,        voiceActivityDetection: 0    };    //ServerPeerConn.createOffer(function (desc) {    ServerPeerConn.createOffer(offerOptions).then(function (desc) {        console.log('createOffer: ' + desc.sdp);        ServerPeerConn.setLocalDescription(desc, function () {            var sdata = {};            sdata["command"] = "__Offer";            sdata["desc"] = desc;            ws_Send(sdata);        },        function (errorInformation) {            console.log('setLocalDescription error: ' + errorInformation);        });    },    function (error) {        console.log(error);    },    offerOptions);}//信令服务function ws_Send(data) {    try {        rtc_ws.send(JSON.stringify(data));    }    catch (ex) {        console.log("Message sending failed!");    }}function wsconnect() {    rtc_ws = new WebSocket("ws://" + host + ":9000");    rtc_ws.onopen = function () {        var sdata = {};        sdata["command"] = "__UserJoin";        var udata = {};        udata["UserID"] = UserID;        udata["Password"] = Password;        sdata["udata"] = udata;        ws_Send(sdata);        console.log("Socket connected!");        labe2.innerHTML = "Socket connected!";    };    rtc_ws.onclose = function () {        console.log("webSocket connection has been disconnected!");        rtc_ws = null;    };    rtc_ws.onmessage = function (Message) {        var obj = JSON.parse(Message.data);        var command = obj.command;        switch (command) {            case "__OnLoginSuccessful": {                createPeerConnection();                cursocketId = obj.socketId;            }            break;            case "__OnError": {                labe2.innerHTML = obj.info;            }            break;            case "__OnSuccessAnswer": {                if (ServerPeerConn) {                    console.log("OnSuccessAnswer[remote]: " + obj.sdp);                    ServerPeerConn.setRemoteDescription(                    new nativeRTCSessionDescription({ type: "answer", sdp: obj.sdp }),                    function () { },                    function (errorInformation) {                        console.log('setRemoteDescription error: ' + errorInformation);                    });                }            }            break;            case "__OnIceCandidate": {                if (ServerPeerConn) {                    console.log("OnIceCandidate[remote]: " + obj.sdp);                    var c = new nativeRTCIceCandidate({                        sdpMLineIndex: obj.sdp_mline_index,                        candidate: obj.sdp                    });                    ServerPeerConn.addIceCandidate(c);                }            }            break;            default: {                console.log(Message.data);            }        }    };}

效果:
服务端:

这里写图片描述

客户端:

这里写图片描述

原创粉丝点击