九天猎人 发表于 2024-12-28 10:47:29

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

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


文章目次



[*]

[*]@(文章目次)

[*]媒介
[*]一、教程
[*]二、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)pushPool = 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;
      pullVideo.srcObject = event.streams;
      })
      // 打开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 init
npm 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) pullPool = connection;
    // 接收推流消息 发送给拉流
    if(connection == wsPush&&pullPool)pullPool.send(msg);
    // 接收拉流消息 发送给推流
    for(let key in pullPool){
      if(connection == pullPool&&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));
六、效果

推流端
https://i-blog.csdnimg.cn/direct/b22bd16e1034426bb9b4f23fc3134ce2.png
拉流端(点击拉流按钮)
https://i-blog.csdnimg.cn/direct/985b16e2af7946ce92f84c1b8a0ad65d.png
七、备注

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

踩坑路漫漫长@~@

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: webrtc学习----前端推流拉流,局域网socket版,一对多