WebRtc 视频通话(网页对网页)
来源:互联网 发布:点对点传输软件 编辑:程序博客网 时间:2024/05/19 20:20
WebRtc是谷歌2010年以6820万美元收购Global IP Solutions公司而获得的一项技术。2011年5月开放了工程的源代码,在行业内得到了广泛的支持和应用,成为下一代视频通话的标准
下面例子主要是网页对网页端,其实Webrtc不仅仅在网页上,在Android 和IOS都可以的,可以跨平台的
建一个简单的动态WEB项目,首先是网页端,js有点多 但是注释的还比较清楚,index.jsp放在WebContent下面
<%@ page language="java" pageEncoding="UTF-8"%><%@ page isELIgnored="false"%><!DOCTYPE HTML><html><head><title>WEBRTC视频通话</title><meta charset="utf-8" /><meta name="keywords" content="ACGIST的视频应用,webrtc" /><meta name="description" content="使用webrtc实现的网页视频通话。" /><style type="text/css">* { margin: 0; padding: 0; overflow-y: hidden;}body { background-color: rgb(34, 34, 34);}#main { display: none; -webkit-transition-property: rotation; -webkit-transition-duration: 2s; text-align: center; -webkit-transform-style: preserve-3d; width: 1200px; margin: 0 auto; padding: 60px 0;}#localVideo { box-shadow: 0 0 20px #000; width: 600px; display: inline-block;}#remoteVideo { box-shadow: 0 0 20px #000; width: 600px; display: none;}#miniVideo { box-shadow: 0 0 20px #000; width: 300px; display: none;}#footer { position: absolute; bottom: 0; width: 100%; height: 28px; background-color: #404040; color: #fff; font-size: 13px; font-weight: bold; line-height: 28px; text-align: center;}.browser { box-shadow: 0 0 20px #000 inset; width: 400px; margin: 200px auto; padding: 20px; text-align: center; color: #fff; font-weight: bold;}@media screen and (-webkit-min-device-pixel-ratio:0) { #main { display: block; } .browser { display: none; }}</style></head><body ondblclick="fullScreen()"> <div class="browser">对不起暂时只支持google chrome浏览器!</div> <div id="main"> <video id="localVideo" autoplay="autoplay"></video> <video id="remoteVideo" autoplay="autoplay"></video> <video id="miniVideo" autoplay="autoplay"></video> </div> <div id="footer"></div> <script type="text/javascript"> var pc; var main; // 视频的DIV var errorNotice; // 错误提示DIV var socket; // websocket var localVideo; // 本地视频 var miniVideo; // 本地小窗口 var remoteVideo; // 远程视频 var localStream; // 本地视频流 var remoteStream; // 远程视频流 var localVideoUrl; // 本地视频地址 var initiator = ${requestScope.initiator}; // 是否已经有人在等待 var started = false; // 是否开始 var channelReady = false; // 是否打开websocket通道 var channelOpenTime; var channelCloseTime; //连接类 var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; //RTC对话类 var RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; //RTC候选对象 var RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; //获取本地媒体流对象 navigator.getMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); //获取远程媒体流URL var URL = window.webkitURL || window.URL; var mediaConstraints = { "has_audio" : true, "has_video" : true }; // 音频和视频 // 初始化 function initialize() { console.log("初始化"); main = document.getElementById("main"); errorNotice = document.getElementById("errorNotice"); localVideo = document.getElementById("localVideo"); miniVideo = document.getElementById("miniVideo"); remoteVideo = document.getElementById("remoteVideo"); noticeMsg(); openChannel(); getUserMedia(); } // 获取用户的媒体 function getUserMedia() { console.log("获取用户媒体"); navigator.getMedia({ "audio" : true, "video" : true }, onUserMediaSuccess, onUserMediaError); } // 获取用户媒体成功 function onUserMediaSuccess(stream) { var url = URL.createObjectURL(stream); localVideo.style.display = "inline-block"; remoteVideo.style.display = "none"; localVideo.src = url; localVideoUrl = url; localStream = stream; if (initiator) maybeStart(); } // 开始连接 function maybeStart() { if (!started && localStream && channelReady) { setNotice("连接中..."); createPeerConnection(); pc.addStream(localStream); started = true; if (initiator) doCall(); } } //接受显示选择 var mediaConstraints = { optional: [], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true } }; // 开始通话 function doCall() { console.log("开始呼叫"); pc.createOffer(setLocalAndSendMessage, function() {}, mediaConstraints); } function setLocalAndSendMessage(sessionDescription) { pc.setLocalDescription(sessionDescription); sendMessage(sessionDescription); } // 发送信息 function sendMessage(message) { var msgJson = JSON.stringify(message); socket.send(msgJson); console.log("发送信息 : " + msgJson); } // 打开websocket function openChannel() { console.log("打开websocket"); socket = new WebSocket( "wss://localhost:8443/WebrtcDemo/video/${requestScope.uid}");//WSS是https socket.onopen = onChannelOpened; socket.onmessage = onChannelMessage; socket.onclose = onChannelClosed; socket.onerror = onChannelError(); } // 设置状态 function noticeMsg() { if (!initiator) { setNotice("让别人加入(注意事项查看源码): https://localhost:8443/WebrtcDemo/msg?oid=${requestScope.uid }"); } else { setNotice("初始化..."); } } // 打开连接 function createPeerConnection() { var server = { "iceServers" : [ { "url" : "stun:localhost" } ] }; pc = new PeerConnection(server); pc.onicecandidate = onIceCandidate; pc.onconnecting = onSessionConnecting; pc.onopen = onSessionOpened; pc.onaddstream = onRemoteStreamAdded; pc.onremovestream = onRemoteStreamRemoved; } // 谁知状态 function setNotice(msg) { document.getElementById("footer").innerHTML = msg; } // 响应 function doAnswer() { pc.createAnswer(setLocalAndSendMessage, function(error) { console.log('Failure callback: ' + error); }); } // websocket打开 function onChannelOpened() { console.log("websocket打开"); channelOpenTime = new Date(); channelReady = true; if (initiator) maybeStart(); } // websocket收到消息 function onChannelMessage(message) { console.log("收到信息 : " + message.data); processSignalingMessage(message.data);//建立视频连接 } // 处理消息 function processSignalingMessage(message) { var msg = JSON.parse(message); if (msg.type === "offer") { if (!initiator && !started) maybeStart(); pc.setRemoteDescription(new RTCSessionDescription(msg)); doAnswer(); } else if (msg.type === "answer" && started) { pc.setRemoteDescription(new RTCSessionDescription(msg)); } else if (msg.type === "candidate" && started) { var candidate = new RTCIceCandidate({ sdpMLineIndex : msg.label, candidate : msg.candidate }); pc.addIceCandidate(candidate); } else if (msg.type === "bye" && started) { onRemoteClose(); } else if (msg.type === "nowaiting") { onRemoteClose(); setNotice("对方已离开!"); } } // websocket异常 function onChannelError(event) { console.log("websocket异常 : " + event); //alert("websocket异常"); } // websocket关闭 function onChannelClosed() { console.log("websocket关闭"); if (!channelOpenTime) { channelOpenTime = new Date(); } channelCloseTime = new Date(); openChannel(); } // 获取用户流失败 function onUserMediaError(error) { console.log("获取用户流失败!"); alert("获取用户流失败!"); } // 邀请聊天:这个不是很清楚,应该是对方进入聊天室响应函数 function onIceCandidate(event) { if (event.candidate) { sendMessage({ type : "candidate", label : event.candidate.sdpMLineIndex, id : event.candidate.sdpMid, candidate : event.candidate.candidate }); } else { console.log("End of candidates."); } } // 开始连接 function onSessionConnecting(message) { console.log("开始连接"); } // 连接打开 function onSessionOpened(message) { console.log("连接打开"); } // 远程视频添加 function onRemoteStreamAdded(event) { console.log("远程视频添加"); var url = URL.createObjectURL(event.stream); miniVideo.src = localVideo.src; remoteVideo.src = url; remoteStream = event.stream; waitForRemoteVideo(); } // 远程视频移除 function onRemoteStreamRemoved(event) { console.log("远程视频移除"); } // 远程视频关闭 function onRemoteClose() { started = false; initiator = false; miniVideo.style.display = "none"; remoteVideo.style.display = "none"; localVideo.style.display = "inline-block"; main.style.webkitTransform = "rotateX(360deg)"; miniVideo.src = ""; remoteVideo.src = ""; localVideo.src = localVideoUrl; setNotice("对方已断开!"); pc.close(); } // 等待远程视频 function waitForRemoteVideo() { if (remoteVideo.currentTime > 0) { // 判断远程视频长度 transitionToActive(); } else { setTimeout(waitForRemoteVideo, 100); } } // 接通远程视频 function transitionToActive() { remoteVideo.style.display = "inline-block"; localVideo.style.display = "none"; main.style.webkitTransform = "rotateX(360deg)"; setTimeout(function() { localVideo.src = ""; }, 500); setTimeout(function() { miniVideo.style.display = "inline-block"; //miniVideo.style.display = "none"; }, 1000); setNotice("连接成功!"); } // 全屏 function fullScreen() { main.webkitRequestFullScreen(); } // 关闭窗口退出 window.onbeforeunload = function() { sendMessage({ type : "bye" }); pc.close(); socket.close(); } // 设置浏览器支持提示信息 function errorNotice(msg) { main.style.display = "none"; errorNotice.style.display = "block"; errorNotice.innerHTML = msg; } if (!WebSocket) { errorNotice("你的浏览器不支持WebSocket!建议使用<a href=\"https://www.google.com/intl/zh-CN/chrome/browser/\" target=\"_blank\">google chrome浏览器!</a>"); } else if (!PeerConnection) { errorNotice("你的浏览器不支持RTCPeerConnection!建议使用<a href=\"https://www.google.com/intl/zh-CN/chrome/browser/\" target=\"_blank\">google chrome浏览器!</a>"); } else { if (window.navigator.userAgent.indexOf("Chrome") !== -1) setTimeout(initialize, 1); // 加载完成调用初始化方法 } </script></body></html>
然后配置Web.xml
<servlet> <servlet-name>VideoMsgServlet</servlet-name> <servlet-class>demo.VideoMsgServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>VideoMsgServlet</servlet-name> <url-pattern>/msg</url-pattern> </servlet-mapping>
VideoMsgServlet,配置入口路径
public class VideoMsgServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void destroy() { super.destroy(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String oid = request.getParameter("oid"); System.out.println("------接受oid------"+oid); // String uid = session.getId(); String uid = System.currentTimeMillis() + ""; System.out.println("------生成uid------"+uid); request.setAttribute("initiator", "false"); if (!RtcVideo.canCreate()) { response.getWriter().write("不能创建通话房间,超过最大创建数量!"); return; } if (!RtcVideo.canJoin(oid)) { response.getWriter().write("对不起对方正在通话中,你不能加入!"); return; } if (RtcVideo.addUser(uid, oid)) { request.setAttribute("uid", uid); } else { request.setAttribute("initiator", "true"); request.setAttribute("uid", uid); request.setAttribute("oid", oid); } request.getRequestDispatcher("/index.jsp").forward(request, response); } public void init() throws ServletException { }}
WebSocket RtcVideo类主要是用于转发Websocket的数据,用于连接握手的时候用
@ServerEndpoint("/video/{uid}")public class RtcVideo { // 最大通话数量 private static final int MAX_COUNT = 20; private static final long MAX_TIME_OUT = 2 * 60 * 1000; // 用户和用户的对话映射 private static final Map<String, String> user_user = Collections.synchronizedMap(new HashMap<String, String>()); // 用户和websocket的session映射 private static final Map<String, Session> sessions = Collections.synchronizedMap(new HashMap<String, Session>()); /** * 打开websocket * @param session websocket的session * @param uid 打开用户的UID */ @OnOpen public void onOpen(Session session, @PathParam("uid")String uid) { System.out.println("------open------"+uid); session.setMaxIdleTimeout(MAX_TIME_OUT); sessions.put(uid, session); } /** * websocket关闭 * @param session 关闭的session * @param uid 关闭的用户标识 */ @OnClose public void onClose(Session session, @PathParam("uid")String uid) { remove(session, uid); System.out.println("------close------"); } /** * 收到消息 * @param message 消息内容 * @param session 发送消息的session * @param uid */ @OnMessage public void onMessage(String message, Session session, @PathParam("uid")String uid) { System.out.println("------message"+message); try { if(uid != null && user_user.get(uid) != null && RtcVideo.sessions.get(user_user.get(uid)) != null) { Session osession = sessions.get(user_user.get(uid)); // 被呼叫的session if(osession.isOpen()) osession.getAsyncRemote().sendText(new String(message.getBytes("utf-8"))); else this.nowaiting(osession); } else { this.nowaiting(session); } } catch (Exception e) { e.printStackTrace(); } } /** * 没有人在等待 * @param session 发送消息的session */ private void nowaiting(Session session) { session.getAsyncRemote().sendText("{\"type\" : \"nowaiting\"}"); } /** * 是否可以继续创建通话房间 * @return 可以:true;不可以false; */ public static boolean canCreate() { return sessions.size() <= MAX_COUNT; } /** * 判断是否可以加入 * @param oid 被申请对话的ID * @return 如果能加入返回:true;否则返回false; */ public static boolean canJoin(String oid) { return !(oid != null && user_user.containsKey(oid) && user_user.get(oid) != null); } /** * 添加视频对象 * @param uid 申请对话的ID * @param oid 被申请对话的ID * @return 是否是创建者:如果没有申请对话ID为创建者,否则为为加入者。创建者返回:true;加入者返回:false; */ public static boolean addUser(String uid, String oid) { if(oid != null && !oid.isEmpty()) { RtcVideo.user_user.put(uid, oid); RtcVideo.user_user.put(oid, uid); return false; } else { RtcVideo.user_user.put(uid, null); return true; } } /** * 移除聊天用户 * @param session 移除的session * @param uid 移除的UID */ private static void remove(Session session, String uid) { String oid = user_user.get(uid); if(oid != null) user_user.put(oid, null); // 设置对方无人聊天 sessions.remove(uid); // 异常session user_user.remove(uid); // 移除自己 try { if(session != null && session.isOpen()) session.close(); // 关闭session } catch (IOException e) { e.printStackTrace(); } }}
部署到本地Tomcat服务器
效果
https://localhost:8080/WebrtcDemo/msg
https://localhost:8443/WebrtcDemo/msg?oid=1493716719713
注意 我的这个是Https,所有用的是Websocket WSS:,Https需要配置ketstore,如果用http,就用WS,端口默认的是8080,也可以运行的!
部分源码Copy网上,单绝无盗版之意,谢谢!!如有不懂,在下面评论
1 0
- WebRtc 视频通话(网页对网页)
- webrtc网页视频demo
- android WebRTC视频通话
- WebRTC实现网页版多人视频聊天室
- WebRTC 点对点视频通话系统
- iOS下WebRTC音视频通话(二)-局域网内音视频通话
- iOS下WebRTC音视频通话(三)-音视频通话
- iOS下WebRTC音视频通话(一)
- C#-接入声网SDK实现网页版1对1视频通话以及边录制边上传
- 基于webRTC的网页、android视频通信学习笔记(一)
- WebRtc技术实现网页摄像头录制视频并上传服务器
- java使用websocket和WebRTC视频通话
- Java+WebSocket+WebRTC实现视频通话实例
- webRtc+websocket多人视频通话
- ionic2集成crosswalk使用WebRTC视频通话
- webrtc (3) 使用webrtc Native API实现视频通话
- 网页视频
- crosswalk 快速入门,利用WebRTC(html)开始开发视频通话
- Java中常用的集合---初学者的你不可错过的精编整理
- 基础算法——排序(一)
- oracle 数据库提示ORA-01033:ORACLE initialization or shutdown in process
- Atlas数据库中间件实操
- Ping的通,但是traceroute不通
- WebRtc 视频通话(网页对网页)
- bzoj 4584: [Apio2016]赛艇 dp
- 数据挖掘学习笔记三
- MyBatis关联表映射(一对多、多对多、一对一、多对一)
- HDU4790 Just Random 容斥原理,数学思路题
- 有理数的个数 STL-map实例应用
- JavaWeb学习二(Servlet入门)
- HTTP协议、TCP协议、IP协议和TCP/IP协议区别
- 半闲居士视觉SLAM十四讲笔记(2)初识 SLAM- part 1 基础理论