WebRTC Demo

利用WebRTC实现的一个局域网内单向视频的示例。由于是在局域网内,所用没有使用到STUN/TURN 服务器,如果需要则可以使用coturn开源库来搭建。然后使用Node.js搭建了一个简单的信令服务器。运行时,需要先打开接收端页面,再打开发送端页面,才能正常看到视频流。

下面是主要部分的代码:

服务器端 

server.js:

'use strict'

var http = require('http');
var https = require('https');
var fs = require('fs');

var serveIndex = require('serve-index');

var express = require('express');

//socket.io
var socketIo = require('socket.io');

var app = express();
//顺序不能换
app.use(serveIndex('./public'));
app.use(express.static('./public'));


var options = {
  key: fs.readFileSync('./cert/oxbbnb.top_nginx/oxbbnb.top.key'),
  cert: fs.readFileSync('./cert/oxbbnb.top_nginx/oxbbnb.top_bundle.pem')
}

var https_server = https.createServer(options, app);
//bind socket.io with https_server
var io = socketIo(https_server);

// 转发双方的SDP和ICE
io.on('connection', (socket) => {
  console.log('a user connected');

  socket.on('send_sdp', (msg) => {
    console.log('send_sdp:\n' + msg.slice(0, 50) + '...\n');
    io.emit('send_sdp', msg);
  });

  socket.on('recv_sdp', (msg) => {
    console.log('recv_sdp:\n' + msg.slice(0, 50) + '...\n');
    io.emit('recv_sdp', msg);
  });

  socket.on('send_ice', (msg) => {
    console.log('send_ice:\n' + msg + '\n');
    io.emit('send_ice', msg);
  });

  socket.on('recv_ice', (msg) => {
    console.log('recv_ice:\n' + msg + '\n');
    io.emit('recv_ice', msg);
  });

  socket.on('disconnect', () => {
    console.log('user disconnected');
    io.emit('user_disconnected');
  });
});

https_server.listen(443, '0.0.0.0');

var http_server = http.createServer(app);
http_server.listen(8080, '0.0.0.0');

发送端:

sender.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC Sender</title>
</head>

<body>
    <h1>WebRTC Sender</h1>
    <video id="localVideo" autoplay playsinline></video>

    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
    <script src="https://cdn.socket.io/socket.io-3.0.1.js"></script>
    <script src="./js/sender.js"></script>
</body>

</html>

sender.js:

const socket = io();

var pc;

navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then(function (localStream) {
        //document.getElementById('localVideo').srcObject = localStream;

        /*
        const configuration = {
            iceServers: [
                // STUN 服务器
                {
                    urls: 'stun:xxx.xxx.xxx.xxx:3478',
                },
                // TURN 服务器
                {
                    urls: 'turn:xxx.xxx.xxx.xxx:3478',
                    username: 'xxx',
                    credential: 'xxx'
                }
            ]
        };
        pc = new RTCPeerConnection(configuration);
        */
        
        pc = new RTCPeerConnection();
        localStream.getTracks().forEach(track => pc.addTrack(track, localStream));

        var offerOptions = {
            offerToReceiveAudio: 1,
            offerToReceiveVideo: 1
        }
        pc.createOffer(offerOptions)
            .then(function (offer) {
                return pc.setLocalDescription(offer);
            })
            .then(function () {
                // 发送SDP
                socket.emit('send_sdp', pc.localDescription.sdp);
                //console.log('send_sdp:\n' + pc.localDescription.sdp);
            });

        pc.onicecandidate = event => {
            if (event.candidate) {
                // 发送ICE
                var data = {
                    type: 'candidate',
                    label: event.candidate.sdpMLineIndex,
                    id: event.candidate.sdpMid,
                    candidate: event.candidate.candidate
                }
                socket.emit('send_ice', JSON.stringify(data));
                //console.log('send_ice:\n' + JSON.stringify(data));
            }
        };
    })
    .catch(function (error) {
        console.error("Error accessing camera:", error);
    });

//接收SDP
socket.on('recv_sdp', (sdp) => {
    //console.log('receive sdp:\n' + sdp);
    pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: sdp }));
})

//接收ICE
socket.on('recv_ice', (ice) => {
    console.log('receive ice:\n' + ice);
    var obj = JSON.parse(ice);
    var iceCandidate = new RTCIceCandidate({
        candidate: obj.candidate, sdpMLineIndex: obj.label, sdpMid: obj.id
    });
    pc.addIceCandidate(iceCandidate);
})

接收端

receiver.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC Receiver</title>
</head>

<body>
    <h1>WebRTC Receiver</h1>
    <video id="remoteVideo" autoplay playsinline></video>

    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
    <script src="https://cdn.socket.io/socket.io-3.0.1.js"></script>
    <script src="./js/receiver.js"></script>
</body>

</html>

receiver.js:

const socket = io();

var pc;

socket.on('send_sdp', (sdp) => {
    //console.log('send_sdp sdp:\n' + sdp);

    /*
    const configuration = {
        iceServers: [
            // STUN 服务器
            {
                urls: 'stun:xxx.xxx.xxx.xxx:3478',
            },
            // TURN 服务器
            {
                urls: 'turn:xxx.xxx.xxx.xxx:3478',
                username: 'xxx',
                credential: 'xxx'
            }
        ]
    };
    pc = new RTCPeerConnection(configuration);
    */  

    pc = new RTCPeerConnection();
    pc.setRemoteDescription(new RTCSessionDescription({ type: 'offer', sdp: sdp }));
    pc.createAnswer().then(function (answer) {
        pc.setLocalDescription(answer);
        socket.emit('recv_sdp', answer.sdp);
    })

    pc.ontrack = (event) => {
        document.getElementById('remoteVideo').srcObject = event.streams[0];
    }
})

socket.on('send_ice', (ice) => {
    console.log('send_ice ice:\n' + ice);
    var obj = JSON.parse(ice);
    var iceCandidate = new RTCIceCandidate({
        candidate: obj.candidate, sdpMLineIndex: obj.label, sdpMid: obj.id
    });
    pc.addIceCandidate(iceCandidate);
    pc.onicecandidate = (e) => {
        if (e.candidate) {
            var data = {
                type: 'candidate',
                label: e.candidate.sdpMLineIndex,
                id: e.candidate.sdpMid,
                candidate: e.candidate.candidate
            }
            socket.emit('recv_ice', JSON.stringify(data));
            //console.log('recv_ice:\n' + JSON.stringify(data));
        }
    }
})

socket.on('user_disconnected', () => {
    console.log('user_disconnected');
    if (pc) {
        pc.close();
        pc = null;
    }
    // 清除视频流
    document.getElementById('remoteVideo').srcObject = null;
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值