webrtc-做的网页视频聊天对话

node-websocket

const Koa = require('koa');
const Router = require('koa-router');
const http = require('http');
const socketIO = require('socket.io');
const fs = require('fs');

//node server.js

const app = new Koa();
const server = http.createServer(app.callback());
const io = socketIO(server, {
  cors: true
});

// 首页路由
let router = new Router();
router.get('/', ctx => {
  ctx.response.type = 'html';
  ctx.response.body = fs.createReadStream('./index.html');
});
app.use(router.routes());

// socket连接
var sockets = {};
io.on('connection', (socket) => {
  console.log('user connected['+socket.id+']');
  sockets[socket.id] = socket;
  //io.emit('message', '我次奥');
  
  var i = 0;
  console.log('有这些客户端:');
  for (var socket_tmpid in sockets) {
	console.log('['+(i+1)+']'+socket_tmpid);
	i++;
  }
  console.log('');
	
  socket.on('message', (data) => {
    console.log(data);
    io.emit('message', data);
  });

  socket.on('swap_candidate', (data) => {
    //console.log('swap_candidate', data);
	for (var socket_tmpid in sockets) {
		if(socket_tmpid != socket.id){
			//console.log('swap_candidate', socket_tmpid, data);
			sockets[socket_tmpid].emit('swap_candidate', data);
		}
	}
  });

  socket.on('swap_offer', (data) => {
    //console.log('swap_offer', data);
	for (var socket_tmpid in sockets) {
		if(socket_tmpid != socket.id){
			console.log('swap_offer', socket_tmpid, data);
			sockets[socket_tmpid].emit('swap_offer', data);
		}
	}
  });
  
  socket.on('swap_answer', (data) => {
    //console.log('swap_answer', data);
	for (var socket_tmpid in sockets) {
		if(socket_tmpid != socket.id){
			console.log('swap_answer', socket_tmpid, data);
			sockets[socket_tmpid].emit('swap_answer', data);
		}
	}
  });
	
  socket.on('disconnect', () => {
    console.log('user disconnected['+socket.id+']');
	delete sockets[socket.id];
  });
});

// 监听端口
server.listen(5544, () => {
  console.log(`server run at: http://localhost:${5544}`)
});

  

前端界面模板:index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebRTC</title>
    <style>
      video {
        width: 300px;
        height: 180px;
        background-color: #f1f1f1;
      }
      .center {
        text-align: center;
      }
      .mt40 {
        margin-top: 40px;
      }
      #btn-chat {
        font-size: 18px;
      }
      
	  #canvas{
	  	border:2px solid red;
	  }
	  #local-video{
	  	border:2px solid green;
	  }
	  #remote-video{
	  	border:2px solid blue;
	  }
	  #remote-img{
	  	border:2px solid yellow;
	  }
    </style>
  </head>
  <body>
    <div id="chat-container" style="display: block">
      <h2>视频通话</h2>
      <div class="center">
	  <table>
		  <tr>
		  	<td>本地视频:</td>
		  	<td><video id="local-video" autoplay></video></td>
		  	<td></td>
		  </tr>
		  <tr>
		  	<td>远程视频:</td>
		  	<td><video id="remote-video" autoplay></video></td>
		  	<td></td>
		  </tr>
	  </table>
      </div>
    </div>
    <script src="/socket.io/socket.io.min.js"></script>
    <script>
	const configuration = {
		    iceServers: [
		        {
		            urls: 'stun:stun.l.google.com:19302'
		        },
		        {
		            urls: 'turn:your-turn-server-url',
		            username: 'your-username',
		            credential: 'your-password'
		        }
		    ]
	};
	
	let localPeerConnection = new RTCPeerConnection(configuration);  // local peer
    let localStream;
    
	const isEmpty = (obj) => {
        return Object.keys(obj).length === 0 && Object.getPrototypeOf(obj) === Object.prototype;
    }
	
	const createLocalMediaStream = async () => {
		try{
			await navigator.mediaDevices.getUserMedia({
	            video: true,
	            audio: false,
	        }).then(localStream => {
	            console.log('获取摄像头数据成功...');
	            conectOtherClient(localStream);
	        });
		}
		catch(err){
			await navigator.mediaDevices.getDisplayMedia({
	            video: true,
	            audio: false,
	        }).then(localStream => {
	            console.log('获取共享桌面数据成功...');
	            conectOtherClient(localStream);
	        });
		}
	}
	
	const conectOtherClient = async (localStream) => {
        var local_video = document.getElementById("local-video");
        local_video.srcObject = localStream;
        
        // 监听 ice 候选项事件
        localPeerConnection.onicecandidate = (event) => {
          if (event.candidate) {
        	socket.emit('swap_candidate', JSON.stringify(event.candidate));
          }
        }

        // 添加本地媒体流
        localStream.getTracks().forEach((track) => {
        	localPeerConnection.addTrack(track, localStream)
        })
        
        //媒体协商(交换SDP)
        const offer = await localPeerConnection.createOffer({offerToReceiveAudio: true, offerToReceiveVideo: true});
        await localPeerConnection.setLocalDescription(offer);
        socket.emit('swap_offer', JSON.stringify(offer));
	};
	
	const swap_offer = async (data) => {
	    await localPeerConnection.setRemoteDescription(data);
        
        const answer = await localPeerConnection.createAnswer();
        await localPeerConnection.setLocalDescription(answer);
        socket.emit('swap_answer', JSON.stringify(answer));
	}
	
	const swap_answer = async (data) => {
	    await localPeerConnection.setRemoteDescription(data);
	}
	
	// 初始化socket事件
	socket = io('ws://127.0.0.1:5544');
	
	socket.on("connect", () => {
		console.log('socket.on.connect: '+socket.id);
		console.log('socket.on.connect: '+socket.connected);
	});
	
	socket.on("disconnect", () => {
		console.log('socket.on.disconnect: '+socket.id); // undefined
	    console.log('socket.on.disconnect: '+socket.connected); // false
	});
	
	socket.on('message', (data) => {
	    console.log('socket.on.message: '+data);
	});
	
	socket.on('swap_candidate', (data) => {
		data = JSON.parse(data);
	    //console.log('socket.on.swap_candidate: ');
	    //console.log(data);
	    
	    localPeerConnection.addIceCandidate(data); // 设置 ice 候选项
        
        // 监听获取媒体数据(前提是peerA已添加了媒体流数据)
        localPeerConnection.ontrack = (event) => {
          document.getElementById('remote-video').srcObject = event.streams[0]
        }
	});
	
	socket.on('swap_offer', (data) => {
		data = JSON.parse(data);
	    console.log('socket.on.swap_offer: ');
	    console.log(data);
	    
	    swap_offer(data);
	});
	
	socket.on('swap_answer', (data) => {
		data = JSON.parse(data);
	    console.log('socket.on.swap_answer: ');
	    console.log(data);
	    
	    swap_answer(data);
	});
	
	createLocalMediaStream();
    </script>
  </body>
</html>

  

运行效果:

1、服务端

 

 

 

客户端a:

 

 

客户端b:

 

 

我这只有一个摄像头,所以我传输的流式数据一个是摄像头,一个是共享桌面,

穿透服务器配置的是公开的google服务器,没有https所以只能在本地跑,不过效果挺好的

 

posted @ 2025-07-04 17:40  河北大学-徐小波  阅读(259)  评论(0)    收藏  举报