当前位置:首页 » 《关注互联网》 » 正文

webrtc学习----前端推流拉流,局域网socket版,一对多

10 人参与  2024年12月23日 14:00  分类 : 《关注互联网》  评论

点击全文阅读


提示:局域网socket版,一对多

文章目录

@[TOC](文章目录) 前言一、教程二、webrtc工作流程三、推流端四、拉流五、socket服务六、效果七、备注总结

前言

‌‌‌‌‌WebRTC(Web Real-Time Communication)‌是一种实时通讯技术,允许网络应用或站点在不借助中间媒介的情况下,建立浏览器之间的点对点(Peer-to-Peer)连接,实现视频流、音频流或其他任意数据的传输。WebRTC的核心功能包括音视频的采集、编解码、网络传输和显示等‌

WebRTC的技术特点
‌1、实时通信‌:WebRTC专注于实时通信,包括音频、视频和其他数据传输‌。
‌2、点对点通信‌:WebRTC支持点对点通信,即两个浏览器之间直接建立连接,无需通过中间服务器‌。
‌3、多媒体引擎‌:WebRTC包含一个多媒体引擎,处理音频和视频流,并提供丰富的API和协议‌。
4‌、NAT穿越‌:WebRTC提供机制,使得在NAT(Network Address Translation)和防火墙等网络设备背后进行通信更为容易‌。
‌5、TURN服务器‌:当P2P连接无法建立时,WebRTC会利用TURN服务器进行数据中转,确保通信的稳定性‌

一、教程

webrtc文档

二、webrtc工作流程

// 推流拉流过程/**  * 推流端获取视频stream  * 推流端生成offer    * 推流端通过offer设置推流LocalDescription  * 推流端发送offer给(拉)流端  * (拉)流端接收offer  * (拉)流端通过offer设置(拉)流端RemoteDescription  * (拉)流端生成answer  * (拉)流端通过answer设置(拉)流端LocalDescription  * (拉)流端发送answer给推流端  * 推流端接收answer设置推流端RemoteDescription  * 推流端发送candidate(video,audio各一次)  * (拉)流端接收candidate  * (拉)流端发送candidate(video,audio各一次)  * 推流端接收candidate  * **/

三、推流端

一个拉流RTCPeerConnection,对应一个推流RTCPeerConnection
X 个拉流RTCPeerConnection,对应X 个推流RTCPeerConnection

push.html

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>推流</title></head><body>  <video id="webrtcVideo" autoplay></video>  <script>    const video = document.getElementById('webrtcVideo');    // webscoket    const ws = new WebSocket('ws://127.0.0.1:1990'); // 可换成局域网ip地址    let videoStream;    // 一个拉流RTCPeerConnection对应一个推流RTCPeerConnection,xx个拉流RTCPeerConnection,对应xx个推流RTCPeerConnection    const pushPool = {};    // rtc connection    let pushRtcCon;    // 打开摄像头,video标签播放视频流    const getStream = async () => {      if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia)console.log('不支持:getUserMedia');      const stream = await navigator.mediaDevices.getUserMedia({video:true});      video.srcObject = stream;      videoStream = stream;    }    getStream();    // 开始推流    const startPush = (pullId) => {      if(!pushPool[pullId])pushPool[pullId] = pushRtcCon = new RTCPeerConnection();      // rtc connection 添加track      videoStream.getVideoTracks().forEach(track => {        pushRtcCon.addTrack(track,videoStream);      });      // 监听icecandidate      pushRtcCon.onicecandidate = (event)=>{        if(event.candidate)ws.send(JSON.stringify({type:'candidate',candidate:event.candidate,id:pullId}))      }      // 创建offer      pushRtcCon.createOffer().then(offer=>{        console.log(offer)        // 设置推流LocalDescription        pushRtcCon.setLocalDescription(offer).then(()=>{ console.log('推流设置LocalDescription成功');});        // offer信息发送给拉流        ws.send(JSON.stringify({type:'offer',id:pullId,offer}))      });    }    // 开启websocket服务    ws.addEventListener('open',()=>{      // 初始化推流通道      ws.send(JSON.stringify({type:'push_init'}))      console.log('websocket连接成功')    });    // 接收wenbscoket信息    ws.addEventListener('message', (event) => {      let data = JSON.parse(event.data);      console.log(data)      // 接收到拉流传来的answer 设置推流RemoteDescription      if(data.type == 'answer')pushRtcCon.setRemoteDescription(data.answer).then(()=>{ console.log('推流设置RemoteDescription成功');});      // 接收拉流candidate 推流rtc connection 添加IceCandidate      if(data.type == 'candidate'&&data.candidate)pushRtcCon.addIceCandidate(data.candidate).then(()=>{ console.log('推流添加candidate成功');});      // 接收拉流开启消息 开始推流      if(data.type == 'pull_start')startPush(data.id);    })  </script></body></html>

四、拉流

pull.html

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title></head><body>  <video id="pullVideo" autoplay preload muted></video>  <div id="pullBtn">拉流</div>  <script>    const pullBtn = document.getElementById('pullBtn');    // 开始拉流    const startPll = () =>{      let ws = new WebSocket('ws://127.0.0.1:1990'); // 可换成局域网ip地址      const pullVideo = document.getElementById('pullVideo');      let pullStrem;      // 拉流rtc connection      const pullRtcCon = new RTCPeerConnection();      const pullID = new Date().getTime()+'io'+Math.round(Math.random()*10000);      // 拉流监听icecandidate      pullRtcCon.onicecandidate = (event)=>{        // 接收到icecandidate  发送candidate给推流端        if(event.candidate)ws.send(JSON.stringify({type:'candidate',candidate:event.candidate,num:1,id:pullID}))      }      // 监听track      pullRtcCon.addEventListener('track' ,(event) => {        pullStrem = event.streams[0];        pullVideo.srcObject = event.streams[0];      })      // 打开webscoket      ws.addEventListener('open',async ()=>{        await ws.send(JSON.stringify({type:'pull_init',id:pullID}));        // 通知推流端,开始推流        ws.send(JSON.stringify({type:'pull_start',id:pullID}));        console.log('websocket连接成功')      });      // 监听webscoket消息      ws.addEventListener('message',(event)=>{        let data = JSON.parse(event.data);        // 接收到推流端offer        console.log(data,'????')        if(data.type == 'offer'){          // 设置拉流端 RemoteDescription          pullRtcCon.setRemoteDescription(data.offer).then(()=>{            console.log('拉流设置RemoteDescription成功')            // 创建answer            pullRtcCon.createAnswer(data.offer).then((answer)=>{              // 设置拉流的LocalDescription              pullRtcCon.setLocalDescription(answer).then(()=>{                console.log('拉流设置LocalDescription成功')              });              // 发送answer到推流端              ws.send(JSON.stringify({type:'answer',answer,id:pullID}))            });          });        }        // 接收推流端candidate  拉流端添加IceCandidate        if(data.type == 'candidate')pullRtcCon.addIceCandidate(data.candidate).then(()=>{ console.log('拉流添加candidate成功');});      })    }    // 拉流按钮点击事件    pullBtn.addEventListener('click',startPll)  </script></body></html>

五、socket服务

安装依赖

npm initnpm install nodejs-websocket -S

index.js

const ws = require('nodejs-websocket');const port = '1990';// 推流通道  拉流通道let wsPush,wsPull,pullPool={};const server = ws.createServer((connection)=>{  // websocket 连接接收数据  connection.on('text',(msg)=>{    let data = JSON.parse(msg);    // 初始化推流websocket    if(data.type == 'push_init')wsPush = connection;    // 初始化拉流websocket    if(data.type == 'pull_init')if(!pullPool[data.id]) pullPool[data.id] = connection;    // 接收推流消息 发送给拉流    if(connection == wsPush&&pullPool[data.id])pullPool[data.id].send(msg);    // 接收拉流消息 发送给推流    for(let key in pullPool){      if(connection == pullPool[key]&&wsPush)wsPush.send(msg);    }  })  // websocket 关闭  connection.on('close',()=>{    wsPush = null;wsPull = null;    console.log('通道关闭')  })  // websocket 报错  connection.on('err',(err)=>{    wsPush = null;wsPull = null;    console.log('通道报错:'+err)  })})server.listen(port,console.log('ws启动成功,127.0.0.1:'+port));

六、效果

推流端
在这里插入图片描述
拉流端(点击拉流按钮)
在这里插入图片描述

七、备注

1、socket地址可换成局域网IP地址访问
2、pull来流请求地址可换成局域网IP地址访问

总结

踩坑路漫漫长@~@


点击全文阅读


本文链接:http://zhangshiyu.com/post/205697.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1