admin管理员组文章数量:1567021
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/intl/zh-CN/chrome/browser/\" target=\"_blank\">google chrome浏览器!</a>");
} else if (!PeerConnection) {
errorNotice("你的浏览器不支持RTCPeerConnection!建议使用<a href=\"https://www.google/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网上,单绝无盗版之意,谢谢!!如有不懂,在下面评论
版权声明:本文标题:WebRtc 视频通话(网页对网页) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1725802224a1043671.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论