• 采集本地视频
function start(){if (!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia) {            console.log("getUserMedia is not supported!")return;} else {//1 ===============配置音视频参数===============var constraints={video:true,audio: true}navigator.mediaDevices.getUserMedia(constraints).then(getMediaStream).catch(handleError)}
}
  • 扑捉本地视频
// 扑捉本地视频
function getMediaStream(stream){localStream =stream;//2 ===============显示本地视频===============localVideo.srcObject = localStream;//这个函数的调用时机特别重要 一定要在getMediaStream之后再调用,否则会出现绑定失败的情况conn();
}
  • 创建socket连接和监听

function conn(){//1 触发socke连接socket = io.connect();//2 加入房间后的回调socket.on('joined',(roomid,id)=>{state = 'joined';createPeerConnection();btnConn.disabled = true;btnLeave.disabled =false;console.log("reveive joined message:state=",state); });socket.on('otherjoin',(roomid,id)=>{if (state === 'joined_unbind') {createPeerConnection();}state = 'joined_conn';//媒体协商call();console.log("reveive otherjoin message:state=",state);   });socket.on('full',(roomid,id)=>{console.log('receive full message ', roomid, id);closePeerConnection();closeLocalMedia();state = 'leaved';btnConn.disabled = false;btnLeave.disabled = true;console.log("reveive full message:state=",state);alert("the room is full!");});socket.on('leaved',(roomid,id)=>{state = 'leaved';socket.disconnect();btnConn.disabled = false;btnLeave.disabled = true;console.log("reveive leaved message:state=",state);});socket.on('bye',(roomid,id)=>{state = 'joined_unbind';closePeerConnection();console.log("reveive bye message:state=",state); });socket.on('disconnect', (socket) => {console.log('receive disconnect message!', roomid);if(!(state === 'leaved')){closePeerConnection();closeLocalMedia();}state = 'leaved';});socket.on('message',(roomid,id,data)=>{//媒体协商if(data){if(data.type === 'offer'){pc.setRemoteDescription(new RTCSessionDescription(data));pc.createAnswer().then(getAnswer).catch(handleAnswerError);}else if(data.type === 'answer'){console.log("reveive client message=====>",data);pc.setRemoteDescription(new RTCSessionDescription(data));}else if(data.type === 'candidate'){var candidate = new RTCIceCandidate({sdpMLineIndex:data.label,candidate:data.candidate});pc.addIceCandidate(candidate);}else{console.error('the message is invalid!',data)}}console.log("reveive client message",roomid,id,data);  });//加入房间socket.emit('join',roomid);return;
}
  • 创建本地流媒体链接
//创建本地流媒体链接
function createPeerConnection(){console.log('create RTCPeerConnection!');if(!pc){pc = new RTCPeerConnection(pcConfig);pc.onicecandidate = (e) =>{if(e.candidate){sendMessage(roomid,{type:'candidate',label:e.candidate.sdpMLineIndex,id:e.candidate.sdpMid,candidate:e.candidate.candidate});}}pc.ontrack = (e)=>{remoteVideo.srcObject = e.streams[0];}}if(pc === null || pc === undefined){console.error('pc is null or undefined!');return;}if(localStream === null || localStream === undefined){console.error('localStream is null or undefined!');return;}if(localStream){localStream.getTracks().forEach((track)=>{pc.addTrack(track,localStream);})}
}//使用turn穿越
var pcConfig={'iceServers':[{'urls':'turn:121.41.76.43:3478','credential':'123456','username':'huang'}]}
  • 接收远端流通道
//接收远端流通道
function call(){if(state === 'joined_conn'){if(pc){var options = {offerToReceiveAudio:1,offerToReceiveVideo:1}pc.createOffer(options).then(getOffer).catch(handleOfferError);}}
}
  • 关闭本地媒体流链接
//关闭本地媒体流链接
function closePeerConnection(){console.log('close RTCPeerConnection!');if(pc){pc.close();pc = null;}
}

效果

源码

  • html
<html><head><title>WebRTC PeerConnection</title><style>.preview{display: flex;}.remote{margin-left: 20px;}</style></head><body><div><div><button id="connserver">Connect Sig Server</button><button id="leave" disabled>Leave</button></div><div class="preview"><div><h2>Local:</h2><video id="localvideo" autoplay playsinline></video></div><div class="remote"><h2>Remote:</h2><video id="remotevideo" autoplay playsinline></video></div></div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script><script src="https://webrtc.github.io/adapter/adapter-latest.js"></script><script src="./js/client.js"></script></body>
</html>
  • js
'use strict'var localVideo = document.querySelector('video#localvideo');
var remoteVideo = document.querySelector('video#remotevideo');var btnConn = document.querySelector('button#connserver');
var btnLeave = document.querySelector('button#leave');var localStream = null;var roomid = '111111';
var socket =null;var state = 'init';var pc = null;var pcConfig={'iceServers':[{'urls':'turn:121.41.76.43:3478','credential':'123456','username':'huang'}]}function sendMessage(roomid,data){if(socket){socket.emit('message',roomid,data);}
}function getAnswer(desc){pc.setLocalDescription(desc);sendMessage(roomid,desc);
}function handleAnswerError(err){console.error('Failed to get Answer!',err);
}function getOffer(desc){pc.setLocalDescription(desc);sendMessage(roomid,desc)
}
function handleOfferError(err){console.error('Failed to get Offer!',err);
}//接收远端流通道
function call(){if(state === 'joined_conn'){if(pc){var options = {offerToReceiveAudio:1,offerToReceiveVideo:1}pc.createOffer(options).then(getOffer).catch(handleOfferError);}}
}// 第一步:开始服务
function connSignalServer(){//开启本地视频start();return true;
}function conn(){//1 触发socke连接socket = io.connect();//2 加入房间后的回调socket.on('joined',(roomid,id)=>{state = 'joined';createPeerConnection();btnConn.disabled = true;btnLeave.disabled =false;console.log("reveive joined message:state=",state);    });socket.on('otherjoin',(roomid,id)=>{if (state === 'joined_unbind') {createPeerConnection();}state = 'joined_conn';//媒体协商call();console.log("reveive otherjoin message:state=",state);   });socket.on('full',(roomid,id)=>{console.log('receive full message ', roomid, id);closePeerConnection();closeLocalMedia();state = 'leaved';btnConn.disabled = false;btnLeave.disabled = true;console.log("reveive full message:state=",state);alert("the room is full!");});socket.on('leaved',(roomid,id)=>{state = 'leaved';socket.disconnect();btnConn.disabled = false;btnLeave.disabled = true;console.log("reveive leaved message:state=",state);});socket.on('bye',(roomid,id)=>{state = 'joined_unbind';closePeerConnection();console.log("reveive bye message:state=",state); });socket.on('disconnect', (socket) => {console.log('receive disconnect message!', roomid);if(!(state === 'leaved')){closePeerConnection();closeLocalMedia();}state = 'leaved';});socket.on('message',(roomid,id,data)=>{//媒体协商if(data){if(data.type === 'offer'){pc.setRemoteDescription(new RTCSessionDescription(data));pc.createAnswer().then(getAnswer).catch(handleAnswerError);}else if(data.type === 'answer'){console.log("reveive client message=====>",data);pc.setRemoteDescription(new RTCSessionDescription(data));}else if(data.type === 'candidate'){var candidate = new RTCIceCandidate({sdpMLineIndex:data.label,candidate:data.candidate});pc.addIceCandidate(candidate);}else{console.error('the message is invalid!',data)}}console.log("reveive client message",roomid,id,data);  });socket.emit('join',roomid);return;
}// 扑捉本地视频
function getMediaStream(stream){localStream =stream;//2 ===============显示本地视频===============localVideo.srcObject = localStream;//这个函数的调用时机特别重要 一定要在getMediaStream之后再调用,否则会出现绑定失败的情况conn();
}function handleError(err){if(err){console.error("getUserMedia  error:",err); }
}// 第二步:采集本地视频
function start(){if (!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia) {           console.log("getUserMedia is not supported!")return;} else {//1 ===============配置音视频参数===============var constraints={video:true,audio: true}navigator.mediaDevices.getUserMedia(constraints).then(getMediaStream).catch(handleError)}
}//关闭流通道
function closeLocalMedia(){if (localStream&&localStream.getTracks()) {localStream.getTracks().forEach((track)=>{track.stop();   });}localStream = null;
}function leave(){if(socket){socket.emit('leave',roomid);}//释放资源closePeerConnection();closeLocalMedia();btnConn.disabled = false;btnLeave.disabled = true;
}//创建本地流媒体链接
function createPeerConnection(){console.log('create RTCPeerConnection!');if(!pc){pc = new RTCPeerConnection(pcConfig);pc.onicecandidate = (e) =>{if(e.candidate){sendMessage(roomid,{type:'candidate',label:e.candidate.sdpMLineIndex,id:e.candidate.sdpMid,candidate:e.candidate.candidate});}}pc.ontrack = (e)=>{remoteVideo.srcObject = e.streams[0];}}if(pc === null || pc === undefined){console.error('pc is null or undefined!');return;}if(localStream === null || localStream === undefined){console.error('localStream is null or undefined!');return;}if(localStream){localStream.getTracks().forEach((track)=>{pc.addTrack(track,localStream);})}
}//关闭本地媒体流链接
function closePeerConnection(){console.log('close RTCPeerConnection!');if(pc){pc.close();pc = null;}
}btnConn.onclick = connSignalServer;btnLeave.onclick = leave;
  • 服务器源码
'use strict'var http = require('http');
var https = require('https');
var fs = require('fs');var express= require('express');var serveIndex=require('serve-index');var USERCOUNT=3;//命令服务器
var socketIo =require('socket.io');
var log4js = require('log4js');log4js.configure({appenders:{file:{type:'file',filename:'app.log',layout:{type:'pattern',pattern:'%r %p - %m',}}},categories:{default:{appenders:['file'],level:'debug'}}
});
var logger = log4js.getLogger();var app=express();
app.use(serveIndex('./public'));
app.use(express.static('./public'));//http  server
var http_server=http.createServer(app);//-----------------------------------------------------------------------//var options={key:fs.readFileSync('./cert/3435783_huangxiaoguo.club.key'),cert:fs.readFileSync('./cert/3435783_huangxiaoguo.club.pem')
}//https server
var https_server=https.createServer(options,app);//命令服务器绑定https
var io = socketIo.listen(https_server);io.sockets.on('connection',(socket)=>{logger.debug("connection");//转发信息socket.on('message', (room, data)=>{logger.debug("message data "+socket.id+" "+room,data);socket.to(room).emit('message', room, socket.id, data)//房间内所有人,除自己外});//用户加入socket.on('join',(room)=>{logger.debug("join",",room = ", room,",socket.id = ", socket.id);socket.join(room);var myRoom = io.sockets.adapter.rooms[room];var users =(myRoom)?Object.keys(myRoom.sockets).length:0;logger.debug('the number of user in room is:'+users)//处理一对一通信if (users<USERCOUNT) {//给本人回信息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.to(room).emit('joined',room,socket.id);//给房间所有人回// io.in(room).emit('joined',room,socket.id);//除自己所有站点回// socket.broadcast.emit('joined',room,socket.id)});//用户离开socket.on('leave',(room)=>{var myRoom = io.sockets.adapter.rooms[room];var users =(myRoom)?Object.keys(myRoom.sockets).length:0;//users-1logger.debug('the number of user in room is:'+(users-1));socket.to(room).emit('bye',room,socket.id);socket.emit('leaved',room,socket.id);//给本人回信息//socket.emit('leaved',room,socket.id);//给房间除自己以外所有人回//socket.to(room).emit('leaved',room,socket.id)//给房间所有人回//io.in(room).emit('leaved',room,socket.id);//除自己所有站点回//socket.broadcast.emit('leaved',room,socket.id)});
});https_server.listen(443, '0.0.0.0');
http_server.listen(80,'0.0.0.0');

测试地址:https://www.huangxiaoguo.club/peerconnection/room.html

webRTC(十):webrtc 实现web端对端视频相关推荐

  1. webRTC(十四):webrtc 端到端文本聊天

    webrtc 端到端文本聊天是在webRTC(十):webrtc 实现web端对端视频基础上实现的,web端到端纯文本聊天,后面有讲到! 文本传输主要使用的是createDataChannel的api ...

  2. 《WebRTC系列》实战 Web 端支持 h265 硬解

    1.背景 Web 端实时预览 H.265 需求一直存在,但由于之前 Chrome 本身不支持 H.265 硬解,软解性能消耗大,仅能支持一路播放,该需求被搁置. 去年 9 月份,Chrome 发布 M ...

  3. android视频通信和web端,探讨用webrtc在手机和浏览器之间实现音视频实时通信的实施环境...

    探讨用webrtc在手机和浏览器之间实现音视频实时通信的实施环境 Walker.Xu product/develop flow: 技术需求: 任务拆解: 1.android客户端 2.前端js网页客户 ...

  4. 即时通讯开发之网页端实时音视频技术WebRTC

    WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音通话或视频聊天的技术,是谷歌2010年以6820万美元收购Globa ...

  5. WebRTC 实现P2P音视频通话——原生IOS端使用WebRTC实现一对一音视频通话

    IOS端使用WebRTC实现一对一音视频通话 前言 环境 一.环境配置 搭建项目,配置权限,通过CocoaPods安装第三方库 二.音视频通话的实现 音视频通话实现主要分为两部分,信令客户端以及web ...

  6. WebRTC 一对一语音通话中音频端到端分段延迟分析

    WebRTC 一对一语音通话中的音频端到端延迟指从一个音频信号被发送端采集,到同一个信号被接收端播放出来这整个过程的时间.音频端到端延迟由多个阶段组成.音频端到端处理的冲采样.混音.回声和降噪等操作会 ...

  7. 基于webrtc的远程控制系统设计与实现——设备端环境配置

    一.配置前准备 下载一个虚拟机,如:VirtualBox或VMWare,并且下载一个Ubuntu 64的镜像文件,Ubuntu是一个以桌面应用为主的Linux操作系统,该镜像文件用于在虚拟机上配置该操 ...

  8. 【webrtc视频会议的搭建】端到端(E2EE)的加密

    created by 杨金彪 https://xbsoftware.com/blog/video-messaging-apps-with-end-to-end-encryption-and-all-a ...

  9. 【音视频第6天】基础知识-移动端实时音视频直播技术详解和开源工程WebRTC的技术原理和使用浅析

    本文是系列文章中的第1篇,本系列文章的大纲如下: <移动端实时音视频直播技术详解(一):开篇> <移动端实时音视频直播技术详解(二):采集> <移动端实时音视频直播技术详 ...

  10. WebRTC十周年、Space X成功对接国际空间站、TikTok复制品Zynn或有快手支持|Decode the Week...

    >>顺便祝大家六一快乐<< Decode the Week≠音视频技术周刊  01 Space X载人飞船首发成功并与国际空间站对接 图片来源:NASA Ins story 北 ...

最新文章

  1. python怎么安装包-安装python第三方包
  2. 没错,接单就是特简单!
  3. Maven使用详解视频课程——笔记(一)
  4. 数组---进制转换(查表法)
  5. 当你从事不喜欢的事怎么办
  6. linux 的文件软链接隐藏,Linux inode及硬链接软链接详解
  7. [vue] 你知道vue中key的原理吗?说说你对它的理解
  8. System.Net.Mail和System.Web.Mail
  9. ASP.NET常用语句1--20条 非常实用
  10. 高清壁纸|海贼王漫画名场面
  11. android 实现论坛界面,android界面开发之主流UI布局范例
  12. 一图读懂开源协议_一张经典图,开源协议比较
  13. Unity app调试
  14. esp8266接入小爱同学,通过mqtt
  15. Vue——watch选项详解
  16. 【EmailCamel 国外邮件营销、美国邮件群发】邮件到达收件箱系列文章05:免费公共邮箱作为发件人的限制
  17. 了解线性分组码的编码原理并编程实现C语言,线性分组码的编译码(DOC).doc
  18. linux禁用用户账号,linux 如何禁用账号和解除禁用账号
  19. A New Approach for English-Chinese Named Entity Alignment(跨语言实体对齐)
  20. Niushop 物流公司设置

热门文章

  1. 重新测试Python读Excel文件xlsx的语言编码
  2. GitBook建立本地Book及导入别人Book
  3. Outlook分组后,为什么桌面和收件箱无提示
  4. 深睿医疗新产品臻现, AI赋能睿智医疗
  5. Python base64 + AES EBC模式加密
  6. tplink显示网络连接已断开_TP-link无线路由器无法上网排查方案及解决办法(图文教程)...
  7. 名悦集团:高速路上突遇发动机熄火该怎么办
  8. python bar函数循环_python bar函数怎么使用
  9. C++从文件中读取数据,打印(追加打印)至文件
  10. 2022年全国最新消防设施操作员(初级消防设施操作员)模拟题及答案