admin管理员组

文章数量:1532285

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