首页 » 软件开发 » 使用webrtc+node.js搭建多人视频会议直播系统(音视频连接开发获取房间)

使用webrtc+node.js搭建多人视频会议直播系统(音视频连接开发获取房间)

雨夜梧桐 2024-07-24 01:24:22 0

扫一扫用手机浏览

文章目录 [+]

npm installnode app.js // 默认端口https://localhost:3000全局安装nodemon 用 nodemon app.js // 热更新启动node启动的是https 可以掉起本地获取摄像头权限浏览器输入https://localhost:3000https://localhost:3000/ 获取音视频设备信息https://localhost:3000/room 完整项目 展示本地流 连接远程流https://localhost:3000/mediastream

C++音视频开发学习资料:点击莬费领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

WebRTC 主要提供了三个核心的 API:

使用webrtc+node.js搭建多人视频会议直播系统(音视频连接开发获取房间) 软件开发
(图片来自网络侵删)
getUserMedia:可以获取本地的媒体流,一个流包含几个轨道,比如视频和音频轨道。
RTCPeerConnection:用于建立 P2P 连接以及传输多媒体数据。
RTCDataChannel:建立一个双向通信的数据通道,可以传递多种数据类型。

如果假设,对等体 A 想要与对等体 B 建立 WebRTC 连接,则需要执行以下操作:

Peer A使用 ICE 生成它的 ICE候选者。
在大多数情况下,它需要 NAT(STUN)的会话遍历实用程序或 NAT(TURN)服务器的遍历使用中继。
Peer A 将 ICE候选者 和会话描述捆绑到一个对象中。
该对象在对等体 A 内存储为本地描述(对等体自己的连接信息),并通过信令机制传送给对等体 B.这部分称为要约。
对等体 B 接收该提议并将其存储为远程描述(另一端的对等体的连接信息)以供进一步使用。
对等体 B 生成它自己的 ICE候选者和会话描述,将它们存储为本地描述,并通过信令机制将其发送给对等体A.这部分称为答案。
(注:如前所述,步骤2和3中的ICE候选人也可以单独发送)对等体 A 从对等体 B 接收答案并将其存储为远程描述。
这样,两个对等体都具有彼此的连接信息,并且可以通过 WebRTC 成功开始通信!

// 获取音视频设备初始化本地流function start() { if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { console.log("不支持"); return } else { var deviceId = viodeSource.value; // 音频视频采集 var constraints = { video: { width: 640, height: 480, frameRate: 15,//30, // 帧率 // facingMode: "enviroment" // 摄像头 deviceId: deviceId ? deviceId : undefined // 视频设备id // 设置之后可以在手机上切换摄像头 前置后置切换 }, audio: { noiseSuppression: true, //降噪 echoCancellation: true // 回音消除 } } // 只设置音频 // var constraints = { // video: false, // audio: true // } navigator.mediaDevices.getUserMedia(constraints) .then(gotMediaStream) .then(gotDevices) .catch(handleError) }}function gotMediaStream(stream) { console.log(stream) videoplay.srcObject = stream; //设置视频流 window.stream = stream; // 获取视频约束 var videoTrack = stream.getVideoTracks()[0]; var videoConstraints = videoTrack.getSettings(); constraints.textContent = JSON.stringify(videoConstraints); //打印在页面上 // audioplayer.srcObject = stream; // 只设置音频 return navigator.mediaDevices.enumerateDevices(); // promise返回 then 后接口 then}

C++音视频开发学习资料:点击莬费领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

建立信令服务器

使用node+koa2搭建信令服务器 引入socket.io

npm i socket.io --save

// 服务器端代码const Koa = require('koa');const app = new Koa();const staticFiles = require('koa-static');const path = require("path");// const http = require("http");const https = require("https");const fs = require("fs");const socketIo = require('socket.io');const log4js = require("log4js");let logger = log4js.getLogger();logger.level = "debug";app.use(staticFiles(path.resolve(__dirname, "public")));const options = { key: fs.readFileSync("./server.key", "utf8"), cert: fs.readFileSync("./server.cert", "utf8")};let server = https.createServer(options, app.callback())// const server = http.createServer(app.callback());server.listen(3000, () => { console.log(`开始了localhost:${3000}`)});const io = socketIo(server);io.on('connection', (socket) => { socket.on("join", (room) => { socket.join(room); // var myRoom = io.sockets.adapter.rooms[room]; // 获取当前房间, rooms是Map对象 Map对象中是set对象 踩坑 var myRoom = io.sockets.adapter.rooms.get(room); // get获取Map size获取大小 // 用户数量 var users = myRoom ? myRoom.size : 0; logger.debug('--房间用户数量--',users, 'room',room); // 是一对一的直播 if(users < 9) { // 小于三个人 socket.emit("joined", room, socket.id); if(users>1) { socket.to(room).emit("otherjoin", room, socket.id); } } else { // 大于三 剔除房间 socket.leave(room); socket.emit("full", room, socket.id); // 房间已满 } // 发给自己 // socket.emit("joined", room, socket.id); // 发给除自己之外的这个节点上的所有人 // 给这个站点所有人发 除了自己全部 // socket.broadcast.emit("joined", room, socket.id); // 发给除了自己之外房间内的所有人 // socket.to(room).emit("joined",room, socket.id) // 发给给房间内所有人 // io.in(room).emit('joined', room, socket.id); }); socket.on("leave", (room) => { // var myRoom = io.sockets.adapter.rooms[room]; var myRoom = io.sockets.adapter.rooms.get(room); console.log("myRoom",myRoom); var users = myRoom ? myRoom.size : 0; logger.debug('--房间用户数量离开房间--',users-1); // // socket.broadcast.emit("leaved", room, socket.id); socket.to(room).emit('bye', room, socket.id); socket.emit("leaved", room, socket.id); // socket.leave(room); // io.in(room).emit('joined', room, socket.id); }) socket.on("message", (room, data)=>{ console.log("message",room) // socket.broadcast.emit("message",room, data) socket.to(room).emit("message",room, data) })});

前端代码

C++音视频开发学习资料:点击莬费领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

引入<script src="../js/socket.io.min.js"></script>

//客户端代码// 连接sockitfunction coon() { console.log("coon") socket = io.connect(); // 新用户加入 socket.on("joined", (roomid, id) => { console.log("收到---joined", roomid, id); state = "joined"; createPeerConnecion(); btnConn.disabled = true; btnLeave.disabled = false; console.log("recevie--joined-state--", state); }); // 其他加入 socket.on("otherjoin", (roomid, id) => { console.log("-otherjoin-", roomid, id); // 开始媒体协商 if (state === "joined_unbind") { createPeerConnecion(); // 创建链接并绑定 } state = "joined_conn"; call(); // 媒体协商 console.log("recevie--otherjoin-state--", state); }); // 用户已满 socket.on("full", (roomid, id) => { console.log("-full-", roomid, id); state = "leaved"; console.log("recevie--full-state--", state); socket.disconnect(); // 断开连接 alert("房间已满"); // 有人退出后可以重连 btnConn.disabled = false; btnLeave.disabled = true; }); // 离开 socket.on("leaved", (roomid, id) => { console.log("-leaved-", roomid, id); state = "leaved"; socket.disconnect(); btnConn.disabled = false; btnLeave.disabled = true; console.log("recevie---leaved-state--", state); }); // 离开了 socket.on("bye", (roomid, id) => { console.log("-bye-", roomid, id); state = "joined_unbind"; // 未绑定状态 console.log("bye-state--", state); closePeerConnection(); }); // 服务端不关心,客户端处理这些消息 收到端对端的消息 怎么处理 socket.on("message", (roomid, data) => { console.log("收到了客户端的 message", roomid, data); // 媒体协商 if(data) { if(data.type ==='offer') { // 如果收到是offer 对端已经创建好了 pc.setRemoteDescription(new RTCSessionDescription(data));//create answer pc.createAnswer().then(getAnswer).catch(err=>{console.log("err 创建失败getanswer")}); } else if(data.type ==='answer') { pc.setRemoteDescription(new RTCSessionDescription(data)); } else if(data.type ==='candidate') { var candidate = new RTCIceCandidate({ sdpMLineIndex: data.label, candidate: data.candidate }); // 加入到本端 peercollect pc.addIceCandidate(candidate).then(()=>{console.log('Successed to add ice candidate');}).catch(err=>{console.error(err);}); } else { console.error("the message is invalid!", data); } } }); // 发送消息 socket.emit("join", "111111"); // 写死加入的房间 return;}创建RTCPeerConnection

WebRTC 中,我们通过 RTCPeerConnection建立通信双方的点对点连接,该接口提供了创建,保持,监控,关闭连接的方法的实现。
为了建立连接,我们需要一台信令服务器,用于浏览器之间建立通信时交换各种元数据(信令)。
同时,还需要 STUN 或者 TURN 服务器来完成 NAT 穿透。
连接的建立主要包含两个部分:信令交换和设置 ICE 候选。

C++音视频开发学习资料:点击莬费领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

RTCPeerConnection 常见开源的turn|stun服务器

//stun:stun.l.google.com:19302// stun.xten.com // stun.voipbuster.com // stun.sipgate.net // stun.ekiga.net// stun.ideasip.com// stun.schlund.de// stun.voiparound.com// stun.voipbuster.com// stun.voipstunt.com// stun.counterpath.com// stun.1und1.de// stun.gmx.net// stun.callwithus.com// stun.counterpath.net// stun.internetcalls.com// numb.viagenie.ca

创建peer

let iceServer = { "iceServers": [ // { // "url": "stun:stun.l.google.com:19302" // } ], sdpSemantics: 'plan-b', // sdpSemantics: 'unified-plan', // bundlePolicy: 'max-bundle', // iceCandidatePoolSize: 0 }; // 创建 //兼容浏览器的PeerConnection写法 let PeerConnection = (window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection) // 创建 var peer = new RTCPeerConnection(iceServer);谷歌调试

谷歌调试 : chrome://webrtc-internals

回音消除和降噪

noiseSuppression 在Firefox中运行堪称完美,当关掉这个约束时,我能非常清晰的听到麦克风收

如果你对音视频开发感兴趣,或者对本文的一些阐述有自己的看法,可以在下方的留言框,一起探讨。

标签:

相关文章

语言中的借用,文化交融的桥梁

自古以来,人类社会的交流与发展离不开语言的传播。在漫长的历史长河中,各民族、各地区之间的文化相互碰撞、交融,产生了许多独特的语言现...

软件开发 2025-01-01 阅读1 评论0

机顶盒协议,守护数字生活的新卫士

随着科技的飞速发展,数字家庭逐渐走进千家万户。在这个时代,机顶盒成为了连接我们与丰富多彩的数字世界的重要桥梁。而机顶盒协议,作为保...

软件开发 2025-01-01 阅读1 评论0

语言基础在现代社会的重要性及方法步骤

语言是人类沟通的桥梁,是社会发展的基础。语言基础作为语言学习的基石,对于个人、社会乃至国家的发展具有重要意义。本文将从语言基础在现...

软件开发 2025-01-01 阅读2 评论0

粤语电影,传承文化,点亮时代之光

粤语电影,作为中国电影产业的一朵奇葩,以其独特的地域特色、丰富的文化内涵和鲜明的艺术风格,赢得了广大观众的喜爱。本文将从粤语电影的...

软件开发 2025-01-01 阅读3 评论0

苹果游戏语言,塑造未来娱乐体验的基石

随着科技的飞速发展,游戏产业逐渐成为全球娱乐市场的重要支柱。在我国,游戏产业更是蓬勃发展,吸引了无数玩家和投资者的目光。而在这其中...

软件开发 2025-01-01 阅读1 评论0