【WebRTC】web前端+nodejs测试demo

为了方便测试webrtc通信功能,我写了一个简单的测试demo分享一下

1.服务端使用nodejs搭建,版本不限。使用的第三方module为expressnodejs-websocket可以使用npm命令来安装

服务端实现了简单的信令协商,代码如下:

// server.js
var ws = require("nodejs-websocket");
var fs = require('fs');
var https = require('https')
var express = require('express');
var app = express();

var userList = {};

var options = {
    secure: true,
    key: fs.readFileSync('ssl-cert-test.key'),
    cert: fs.readFileSync('ssl-cert-test.pem')
};

app.use(express.static(__dirname));
app.get('/', (req, res) => res.send('Hello World!'));
https.createServer(options, app).listen(1443);
 
var server = ws.createServer(options, function (conn) {
    console.log("New connection");
    conn.on("text", function (str) {
        var obj = JSON.parse(str);
        if (obj.action == "login" && obj.name != undefined) {
            if (obj.name in userList) {
                userList[obj.name].username = null;
                delete userList[obj.name];
            }

            userList[obj.name] = conn;
            conn.username = obj.name;
            console.log("User login: " + obj.name);
        }
        else if (obj.action == "forward" && obj.to != undefined) {
            if (obj.to in userList) {
                var tConn = userList[obj.to];
                tConn.sendText(obj.msg);

                console.log(conn.username + " forward to: " + obj.to);
            }
        }
        else if (obj.action == "logout" && obj.name != undefined) {
            delete userList[conn.username];
        }
    })
    conn.on("close", function (code, reason) {
        console.log("Connection closed: " + code);
        if (conn.username != null || conn.username != undefined) {
            delete userList[conn.username];
            console.log("User closed: " + conn.username);
        }
    });
    conn.on("error", function (err) {
        console.log("Connection err: " + err);
    });
}).listen(8001);

需要注意的是服务端提供的是 https 以及 wss 服务(走TLS),因此要用到证书和私钥文件。我们自己可以使用openssl来自签名一个证书,具体方法可以自行搜索相关文章。

2.web前端测试代码:

<!doctype html>

<html>
<head>
  <title>WebSocket Chat Demo with WebRTC Calling</title>
  <meta charset="utf-8">
  <script>
    var PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    var SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
    var GET_USER_MEDIA = navigator.getUserMedia ? "getUserMedia" :
                     navigator.mozGetUserMedia ? "mozGetUserMedia" :
                     navigator.webkitGetUserMedia ? "webkitGetUserMedia" : "getUserMedia";
    var v = document.createElement("video");
    var SRC_OBJECT = 'srcObject' in v ? "srcObject" :
                 'mozSrcObject' in v ? "mozSrcObject" :
                 'webkitSrcObject' in v ? "webkitSrcObject" : "srcObject";

    // url后面加 ‘#’ 的,认为是发起的offer提供者
    var isCaller = window.location.href.indexOf('#') != -1;

    ///
    // webrtc必须使用https,而websocket也需要使用https,并且会验证证书有效性,因此自己颁发了一套证书,并且绑定域名 *.test.com,测试时hosts文件还需要修改一下,将 wss.test.com 指定到server.js 运行的ip地址
    var socket = new WebSocket("wss://wss.test.com:8001");
    socket.onopen = function() {
        if (isCaller) {
            socket.send(JSON.stringify({
                "action": "login",
                "name": "caller"
            }));
        }
        else {
            socket.send(JSON.stringify({
                "action": "login",
                "name": "answer"
            }));
        }
    };

    /*
    var socket2 = new WebSocket("wss://wss.test.com:8001");
    socket2.onopen = function() {
        socket2.send(JSON.stringify({
            "action": "login",
            "name": "caller2"
        }));
    };

    socket2.onmessage = function(event) {
        var msg = JSON.parse(event.data);
        console.log(msg);
    }
    */
    function forward() {
        socket.send(JSON.stringify({
            "action": "forward",
            "to": "caller2",
            "msg": JSON.stringify({
                "event": "offer",
                "sdp": "xxxxxx"
            })
        }));
    }

    ///
    var iceServers = {
        iceServers: [     // Information about ICE servers - Use your own!
            {
                urls: "turn:webrtc-from-chat.glitch.me",  // A TURN server
                username: "webrtc",
                credential: "turnserver"
            }
        ]
    };
    // 不使用ice协商服务器
    var peerConnection = new PeerConnection();
    // 发送ICE候选到其他客户端
    peerConnection.onicecandidate = function(event){
        if (event.candidate !== null) {
            console.log("ICE: " + JSON.stringify(event.candidate));
            if (!isCaller) {
                var to = isCaller?"answer":"caller";
                socket.send(JSON.stringify({
                    "action": "forward",
                    "to": to,
                    "msg": JSON.stringify({
                        "event": "_ice_candidate",
                        "candidate": event.candidate
                    })
                }));
            }
        }
    };

    peerConnection.ontrack = function(event) {
        document.getElementById('remoteVideo').srcObject = event.streams[0];
        var c = event.track.getConstraints();
        console.log(c);
    };

    var sendOffer = function (desc) {
        // 设置本地Offer
        peerConnection.setLocalDescription(desc); 
        // FIXME 发送sdp给对方
        socket.send(JSON.stringify({
            "action": "forward",
            "to": "answer",
            "msg": JSON.stringify({
                "event": "_offer",
                "sdp": desc
            })
        }));
    }

    var sendAnswer = function (desc) {
        // 设置本地Offer
        peerConnection.setLocalDescription(desc); 
        // FIXME 发送sdp给对方
        socket.send(JSON.stringify({
            "action": "forward",
            "to": "caller",
            "msg": JSON.stringify({
                "event": "_answer",
                "sdp": desc
            })
        }));
    }

    socket.onmessage = function(event) {
        var msg = JSON.parse(event.data);
        console.log(msg);
        //如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述
        if( msg.event === "_ice_candidate" ){
            peerConnection.addIceCandidate(new RTCIceCandidate(msg.candidate));
        } else {
            peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp));
            // 如果是一个offer,那么需要回复一个answer
            if(msg.event === "_offer") {
                peerConnection.createAnswer(sendAnswer, function (error) {
                    console.log('Failure callback: ' + error);
                });
            }
        }
    }

    function openRtc() {
        navigator.getUserMedia({
            audio: true, // 是否开启麦克风
            video: true // 是否开启摄像头,这里还可以进行更多的配置
        }, function(stream){
            // 测试枚举音频轨道
            var tracks = stream.getAudioTracks();

            // 绑定媒体流到video标签用于输出 
            document.getElementById('localVideo').srcObject = stream; 
            // 向PeerConnection中加入需要发送的流 
            peerConnection.addStream(stream);
            /*var transceivers = peerConnection.getTransceivers();
            transceivers.forEach(transceiver => {
                // 生成只接收媒体数据的 sdp
                transceiver.direction = "recvonly";
            });*/
            // 生成offer的选项
            //var offOption = {offerToReceiveVideo: false, offerToReceiveAudio: false};
            if (isCaller) {
                // 如果是发起方则发送一个offer信令
                peerConnection.createOffer(sendOffer, function (error) {
                    console.log('Failure callback: ' + error);
                });
            }
        }, function(error){ 
            // 获取本地视频流失败
            console.log("获取本地视频流失败");
        });
    }
  </script>
</head>
<body>
    <div id="videoContainer">
        <video id="localVideo" autoplay muted></video>
        <video id="remoteVideo" autoplay></video>
    </div>
    <button onclick="openRtc()">click</button>
</body>
</html>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值