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
原创粉丝点击